CAUTION

New projects are no longer recommended to use Reduck. You can use state management tools from the community, such as Jotai, zustand, valtio, etc.

Quick Start

Reduck is a state management library developed by the Modern.js team that follows the MVC pattern. Its underlying state storage is based on Redux implementation, while providing a higher level of abstraction and full compatibility with the Redux ecosystem.

The goal of Reduck is to organize the development structure of React applications in the MVC pattern, maintain business logic in the Model layer, decoupling business logic from UI, making it easier for developers to focus on business logic, and reducing duplicated work (boilerplate code) through higher level of abstraction.

In the MVC pattern, Reduck plays the role of M(Model), React UI Component corresponds to V(View), and gets the Model from Reduck and modifies the Model's React Container Component, which corresponds to C(View Controller/Container).

The state management solution of Modern.js is implemented through built-in Reduck. Using Reduck in Modern.js not only eliminates the manual integration process, but also allows all Reduck APIs to be imported and used directly from the Modern.js Runtime package, providing a better consistency experience.

INFO
  1. To use Reduck APIs in Modern.js, you need to set runtime.state to enable the state management plugin.
  2. Reduck can also be used separately as a state management library outside of Modern.js.

Core Concepts

There are only four core concepts in Reduck: Model, State, Actions, and Effects.

Model: Encapsulates the logic and required state of an independent module, consisting of State, Actions, and Effects.

State: The state stored in the Model.

Actions: Pure functions used to modify State, functions must be Synchronous.

Effects: Functions with side effects used to modify State, functions can be Asynchronous. Effects can call their own Actions and Effects or those of other Models.

The Reduck data flow is shown in the following figure:

Reduck Data Flow

Basic Usage

Next, let's take a simple Counter application as an example to demonstrate the basic usage of Reduck.

First, we define a Model named count:

import { model } from '@modern-js/runtime/model';

const countModel = model('count').define({
  state: {
    value: 1,
  },
});

export default countModel;

We use the API model to create countModel, which currently only contains the state that stores the counter value, that is, value in the code.

We define an action to increase the counter by 1:

import { model } from '@modern-js/runtime/model';

const countModel = model('count').define({
  state: {
    value: 1,
  },
  actions: {
    add(state) {
      state.value += 1;
    },
  },
});

export default countModel;

In the add action, we can directly modify the value of the state and perform the increment operation without treating the state as an immutable object. This is because Reduck integrates immer, which can directly modify the original state object.

Next, we will demonstrate how to use the Model in a component.

Create a new component called Counter, and use the countModel via the useModel API inside the component:

import { useModel } from '@modern-js/runtime/model';
import countModel from './models/count';

function Counter() {
  const [state, actions] = useModel(countModel);

  return (
    <div>
      <div>counter: {state.value}</div>
      <button onClick={() => actions.add()}>add</button>
    </div>
  );
}

useModel gets the state and actions of countModel. The component displays the current value of the counter, and clicking the add button increments the counter by 1.

INFO

Due to the simplicity of the example used here, the layering of the MVC pattern is not strictly followed. The Counter component acts as both the V and C layers.

The final demonstration effect is as follows:

countModel

That completes a simple counter application. You can view the complete example code for this section here.