Skip to content

⛰️ Strongly typed React framework using generators and efficiently updated views alongside the publish-subscribe pattern.

Notifications You must be signed in to change notification settings

Wildhoney/Chizu

Repository files navigation

Strongly typed React framework using generators and efficiently updated views alongside the publish-subscribe pattern.

Contents

  1. Benefits
  2. Getting started
  3. Error handling

Benefits

  • Finely tuned and thoughtful event-driven architecture superset of React.
  • Super efficient with views only re-rendering when absolutely necessary.
  • Built-in support for optimistic updates within components.
  • Mostly standard JavaScript without quirky rules and exceptions.
  • Clear separation of concerns between business logic and markup.
  • First-class support for skeleton loading using generators.
  • Strongly typed throughout – dispatches, models, etc…
  • Easily communicate between actions using distributed actions.
  • Bundled decorators for common action functionality such as consecutive mode.

Getting started

Actions are responsible for mutating the state of the view. In the below example the name is dispatched from the view to the actions, the state is updated and the view is rendered with the updated value. We use the Handlers type to ensure type safety for our actions class.

const model: Model = {
  name: null,
};

export class Actions {
  static Name = createAction<string>();
}

export default function useNameActions() {
  return useActions(
    model,
    <Handlers<Model, typeof Actions>>class {
      [Actions.Name] = utils.set("name");
    },
  );
}
export default function Profile(props: Props): React.ReactElement {
  const [model, actions] = useNameActions();

  return (
    <>
      <p>Hey {model.name}</p>

      <button onClick={() => actions.dispatch(Actions.Name, randomName())}>
        Switch profile
      </button>
    </>
  );
}

You can perform asynchronous operations in the action which will cause the associated view to render a second time – as we're starting to require more control in our actions we'll move to our own fine-tuned action:

const model: Model = {
  name: null,
};

export class Actions {
  static Name = createAction();
}

export default function useNameActions() {
  const nameAction = useAction<Model, typeof Actions, "Name">(
    async (context) => {
      context.actions.produce((draft) => {
        draft.name = null;
      });

      const name = await fetch(/* ... */);

      context.actions.produce((draft) => {
        draft.name = name;
      });
    },
  );

  return useActions(
    model,
    <Handlers<Model, typeof Actions>>class {
      [Actions.Name] = nameAction;
    },
  );
}
export default function Profile(props: Props): React.ReactElement {
  const [model, actions] = useNameActions();

  return (
    <>
      <p>Hey {model.name}</p>

      <button onClick={() => actions.dispatch(Actions.Name)}>
        Switch profile
      </button>
    </>
  );
}

Error handling

Chizu provides a simple way to catch errors that occur within your actions. You can use the ActionError component to wrap your application and provide an error handler. This handler will be called whenever an error is thrown in an action.

import { ActionError } from "chizu";

const App = () => (
  <ActionError handler={(error) => console.error(error)}>
    <Profile />
  </ActionError>
);

About

⛰️ Strongly typed React framework using generators and efficiently updated views alongside the publish-subscribe pattern.

Resources

Stars

Watchers

Forks

Packages

No packages published