State Management tool for solid.js


Keywords
redux, solid-redux, reactive
License
MIT
Install
npm install solid-redux@0.6.0

Documentation

solid-redux

State Management Tools for solid.js. It is just using createSignal and createStore but it's dispatching action under the hood. You can have effects on the actions, debouncing, and selection. This tool provides you with action methods. You don't need to dispatch anything it's automatic. This tool has been designed considering zero boilerplate and ceremony on the state management code base.

Installation

Add the solid-redux package to your project:

# NPM
npm i solid-redux
# YARN
yarn add solid-redux

Demo App

Todo state

import { createReducer, on, PayloadAction } from 'solid-redux';

export interface Todo {
  id: number;
  text: string;
  completed: boolean;
}
export enum Filter {
  ALL,
  COMPLETED,
  ACTIVE,
}
type AppState = { filter: Filter; todos: Todo[]; loading: boolean };

let todoId = 0;
export const {
  actions: { add, update, remove, all, completed, active },
  state: todoState,
} = createReducer({
  name: 'todos',
  initialState: { filter: Filter.ALL, todos: [], loading: true } as AppState,
  reducers: {
    init(setState) {
      add('Cool');
    },
    async add(setState, action: PayloadAction<string>) {
      setState('loading', true);
      await delay(1000);
      setState('todos', (todos) => [
        ...todos,
        { id: ++todoId, text: action.payload, completed: false },
      ]);
      setState('loading', false);
    },
    update(setState, action: PayloadAction<number>) {
      setState(
        'todos',
        (todo) => todo.id === action.payload,
        'completed',
        (completed) => !completed
      );
    },
    remove(setState, action: PayloadAction<number>) {
      setState('todos', (todos) =>
        todos.filter((todo) => todo.id !== action.payload)
      );
    },
    all(setState) {
      setState('filter', Filter.ALL);
    },
    completed(setState) {
      setState('filter', Filter.COMPLETED);
    },
    active(setState) {
      setState('filter', Filter.ACTIVE);
    },
  },
});

export const loadData = createAction('loadData');

on(add, update, remove)
  .debounce(1000)
  .effect((action) => {
    console.log(action);
  });

export const data = on(loadData)
  .debounce(300)
  .select<number>(async (action, data) => {
    data.loading();
    await delay(1000);
    data.success(555);
  }, 0);

function delay(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

Apply todoState in the solid component

import { Component, For } from 'solid-js';
import { add, update, remove, todoState, data, loadData, Filter, all, completed, active } from './todoState';

const Todos: Component = () => {
  let input: HTMLInputElement = null!;
  const todos = createMemo(() => {
    switch (todoState.filter) {
      case Filter.ALL: return todoState.todos
      case Filter.COMPLETED: return todoState.todos.filter(a => a.completed)
      case Filter.ACTIVE: return todoState.todos.filter(a => !a.completed)
      default:
        return todoState.todos
    }
  })

  return (
    <>
      <div>
        <button onClick={() => loadData()}>{data().loading ? 'loading...' : 'Load data'}</button>
        <span>{data().data}</span>
      </div>
      <div>
        <button style={{ color: todoState.filter === Filter.ALL ? 'red' : 'black' }} onClick={[all, null]}>All</button>
        <button style={{ color: todoState.filter === Filter.COMPLETED ? 'red' : 'black' }} onClick={[completed, null]}>Completed</button>
        <button style={{ color: todoState.filter === Filter.ACTIVE ? 'red' : 'black' }} onClick={[active, null]}>Active</button>
      </div>
      <div>
        <input ref={input} />
        <button
          onClick={(e) => {
            if (!input.value.trim()) return;
            add(input.value);
            input.value = "";
          }}
        >
          {todoState.loading ? 'loading...' : 'Add'}
        </button>
      </div>
      <For each={todos()}>
        {(todo) => {
          const { id, text } = todo;
          console.log(`Creating ${text}`)
          return <div>
            <input
              type="checkbox"
              checked={todo.completed}
              onchange={[update, id]}
            />
            <span
              style={{ "text-decoration": todo.completed ? "line-through" : "none" }}
            >{text}</span>
            <span onClick={[remove, id]} style={{ color: 'red', cursor: 'pointer' }}>X</span>
          </div>
        }}
      </For>
    </>
  );
};

export default Todos;