Using Rematch for State Management in React Applications
This guide walks through creating a React Todo List app with Rematch, showing how to define typed models, initialize a Redux‑compatible store, provide it via React‑Redux, and use useSelector and typed useDispatch in components, highlighting Rematch’s concise syntax and similarity to Redux‑Toolkit.
Some readers have expressed confusion about state management when developing React Native applications. In the upcoming series we will compare several popular state‑management frameworks for both React and React Native, highlighting their usage, advantages, and drawbacks.
The previous article introduced the upgraded version of Redux, redux‑toolkit . This article focuses on a well‑known Redux enhancement library called Rematch .
Below is a complete example of building a simple Todo List app with React and Rematch. The full source code is available at the end of the article.
1. Create a new React project
npx create-react-app todolist2. Install Rematch and react‑redux
npm install @rematch/core react-redux3. Define the model types
import { Models } from "@rematch/core";
// Export the type that represents all models in the current business
export interface RootModel extends Models<RootModel> {
// Currently empty
}
// Create and export the model instances
export const models: RootModel = {};In the code above, RootModel describes the type of all models used in the application.
Rematch’s model concept is similar to a Redux‑Toolkit slice : it defines an initial state and the operations that can modify it.
4. Create a todo.ts file and define a Todo model
import { createModel } from "@rematch/core";
import { State, TODO } from "../../module/todo";
import { RootModel } from "./models";
const initState: State = {
todos: [
{
text: "zsx clean room, model init data"
}
]
};
export const todo = createModel<RootModel>()({
name: 'todo',
state: initState,
reducers: {
addTodo: (state: State, payload: string) => {
return {
...state,
todos: [...state.todos, { text: payload }]
};
},
deleteTodo: (state: State, payload: string) => {
const todos = state.todos.filter((item: TODO) => item.text !== payload);
return { ...state, todos };
}
}
});The Rematch model and Redux‑Toolkit slice share the same idea: they each encapsulate a piece of business state and the reducers that operate on it.
5. Add the Todo model to models.ts
import { Models } from "@rematch/core";
import { todo } from "./todo";
export interface RootModel extends Models<RootModel> {
// The key name is used when dispatching actions
todo: typeof todo;
}
export const models: RootModel = { todo };Now the models object contains a todo entry, which can be accessed via store.dispatch.todo and store.getState().todo .
6. Create the store with Rematch’s init function
import { init, RematchDispatch, RematchRootState } from "@rematch/core";
import { models, RootModel } from "./model/models";
export const store = init({
models
});
store.subscribe(() => {
console.log('store update >>> ' + JSON.stringify(store.getState()));
});
store.dispatch.todo.addTodo("add from store");
export type Store = typeof store;
export type Dispatch = RematchDispatch<RootModel>;
export type RootState = RematchRootState<RootModel>;The init function receives the models object and returns a Redux‑compatible store that supports subscribe and dispatch .
7. Provide the store to the React component tree
import RematchTodoApp from './rematch/RematchTodoApp';
import { store } from './rematch/store';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
);8. Build the UI component and use useSelector and useDispatch
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { TODO } from "../module/todo";
import { Dispatch, RootState } from "./store";
const RematchTodoApp = () => {
const todos = useSelector((state: RootState) => state.todo.todos);
const dispatch = useDispatch<Dispatch>();
const [text, setText] = useState('');
const handleAddTodo = () => {
dispatch.todo.addTodo(text);
setText('');
};
const handleDeleteTodo = (text: string) => {
dispatch.todo.deleteTodo(text);
};
return (
This Is Rematch TODO App.
{todos && todos.map((todo: TODO, index: number) => (
{todo.text}
handleDeleteTodo(todo.text)}>finish
))}
setText(e.target.value)} />
Add Todo
);
};
export default RematchTodoApp;In the component we use useSelector to read state.todo.todos and useDispatch (typed with Dispatch ) to dispatch actions such as dispatch.todo.addTodo and dispatch.todo.deleteTodo .
Summary
Managing state with Rematch involves the following steps:
Extend Rematch.Models to define the type of all models used in the project.
Use createModel to define a business‑specific model (e.g., a Todo model) with an initial state and reducers that must return a new state.
Initialize the store with init , passing the collection of models, and export the corresponding Store , Dispatch , and RootState types.
Wrap the application with Provider to make the store available to the component tree.
In UI components, retrieve state with useSelector and dispatch actions with a typed useDispatch .
Rematch closely mirrors Redux‑Toolkit in structure and usage, offering a more direct syntax for reducers (payload instead of action) and eliminating the need to export actions and reducers separately.
Full source code: https://github.com/shixinzhang/redux-sample/tree/main/src/rematch
Ximalaya Technology Team
Official account of Ximalaya's technology team, sharing distilled technical experience and insights to grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.