smx
TypeScript icon, indicating that this package has built-in type declarations

0.0.16 • Public • Published

smx

Create todo app

Create states

// app/states.js
import {state} from 'smx';
// create todoListState with empty array as default value
export const todoListState = state([]);

Define effects to mutate todoListState

// app/effects.js
import {effect} from 'smx';
import {todoListState} from './states';

export const addTodo = effect(
  // effect body is just tuple [state, reducer]
  [
    // specific state need to be mutated
    todoListState,
    // state reducer
    (
      // current state
      state,
      // effect payload
      {text},
    ) =>
      // return next state
      // keep state is immutable
      state.concat({
        id: Math.random(),
        text,
        completed: false,
      }),
  ],
);

export const removeTodo = effect([
  todoListState,
  (state, {id}) => state.filter((x) => x.id !== id),
]);

export const toggleTodo = effect([
  todoListState,
  (state, {id}) =>
    state.map((x) => (x.id === id ? {...x, completed: !x.completed} : x)),
]);

Define filtered todo state

import {state} from 'smx';
export const filteredTodoListState = state((filter) => {
  // access todos from todoListState
  // that means when todoListState changed, filteredTodoListState will change as well
  const todos = todoListState.value();

  switch (filter) {
    case 'active':
      return todos.filter((x) => !x.completed);
    case 'completed':
      return todos.filter((x) => x.completed);
    default:
      return todos;
  }
});

// using state(arg1, arg2, ...argN) to get state family
// active todos
console.log(filteredTodoListState('active').value());
// completed todos
console.log(filteredTodoListState('completed').value());

Loading todo list from server

// convert todoListState to async state
import {state} from 'smx';
// create todoListState with empty array as default value
export const todoListState = state(
  // if we poss async function to state factory, it will create async state
  async () => {
    const data = fetch('api_url').then((res) => res.json());
    return data;
  },
);
console.log(todoListState.value()); // Promise of todo array
// we should update filteredTodoListState
export const filteredTodoListState = state(async (filter) => {
  const todos = await todoListState.value();

  switch (filter) {
    case 'active':
      return todos.filter((x) => !x.completed);
    case 'completed':
      return todos.filter((x) => x.completed);
    default:
      return todos;
  }
});

Handling todoListState changing

import {todoListState} from './states';

todoListState.on(({value}) => console.log('todo-list changed', value));

Handling effects triggering

import {addTodo} from './effects';

addTodo.on(() => console.log('add-todo triggered'));

Display useful info

const countMapper = (todos) => todos.length;
// using map(mapper) to create new state that map current state value to new value.
// When original state changed, mapped state value changed as well
const todoCountState = todoListState.map(countMapper);
// using mapAll(mapper) to apply mapper to all state family
const filteredTodoCountState = filteredTodoListState.mapAll(countMapper);

console.log(await countMapper.value()); // display number of all todos
console.log(await filteredTodoCountState('active').value()); // display number active todos
console.log(await filteredTodoCountState('completed').value()); // display number completed todos

Using generator in effect

We can use effect generator to listen effect triggering or state changing

yield [[ effectOrState1, effectOrState2, ... ]];

Listen multiple single state changing / effect triggering, next statement will run when all effects triggered with specified order

import {effect} from 'smx';

const up = effect(),
  down = effect(),
  left = effect(),
  right = effect(),
  pressA = effect(),
  pressB = effect();

const konamiCodeEpic = effect(function* () {
  // wait until below effects triggered
  yield [[up, up, down, down, left, right, left, right, pressB, pressA]];
  console.log('You have got 30 lives');
});

konamiCodeEpic();

yield { effectOrState1, effectOrState2, ... };

Listen multiple single state changing / effect triggering, next statement will run when one of those effects triggered

const effect1 = effect(),
  effect2 = effect();
const logEpic = effect(function* () {
  const triggeredEffect = yield {effect1, effect2};
  if (triggeredEffect === effect1) {
    console.log('effect1 triggered');
  } else if (triggeredEffect === effect2) {
    console.log('effect2 triggered');
  }
});

yield [{ effectOrState1, effectOrState2, ... }];

Listen multiple single state changing / effect triggering, next statement will run when all effects triggered

const effect1 = effect(),
  effect2 = effect();
const logEpic = effect(function* () {
  yield [{effect1, effect2}];
  console.log('effect1 and effect2 triggered');
});

yield effectOrState;

Listen single state changing / effect triggering

const addTodo = effect();

const logEpic = effect(function* () {
  yield addTodo;
  console.log('new todo added successfully');
});

yield [funcOrEffect, ...args];

Invoke function or effect with specified args

function navigateTo(route) {}

const addTodo = effect();

effect(function* () {
  yield [addTodo, 'first item'];
  yield [navigateTo, '/todo-list'];
});

yield [state, valueOrReducer];

Mutate state

const count = state(0);

effect(function* () {
  yield [count, 1];
  yield [count, (value) => value + 1];
});

yield [ [state1, valueOrReducer], [state2, valueOrReducer], [effect, ...args] ];

Perform multiple actions (state mutation, effect triggering)

const count = state(0);

effect(function* () {
  yield [
    [count, 1],
    [count, (value) => value + 1],
  ];
});

Dependents (0)

Package Sidebar

Install

npm i smx

Weekly Downloads

1

Version

0.0.16

License

ISC

Unpacked Size

187 kB

Total Files

11

Last publish

Collaborators

  • linq2js