Strongly typed React framework using generators and efficiently updated views alongside the publish-subscribe pattern.
- 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.
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>
</>
);
}
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>
);