The actions in the model must be pure functions and cannot have any side effects during execution. However, in real-world scenarios, we often encounter many side effects, such as: requesting data from an HTTP API to obtain state data, or modifying localStorage
or sending events while updating the state. In Reduck, side effects are managed through the model's effects
function.
The most common scenario in which side effects modify the state is requesting data from an HTTP API to update state data.
Let's take a simple todoModel
as an example. It has a side effect function load
that requests the TODO list from a remote server. After the request succeeds, it updates the state.items
field.
The side effect function is uniformly defined under the effects
field. Here we have written a load
function that returns a Promise. After the Promise is successfully executed, it returns the TODO list ["Lerna Modern.js"]
.
Side effect functions need to work with actions to modify state. Therefore, we define a load
object in actions
(the name needs to be consistent with the name of the side effect function under effects
), which includes three actions (pending
, fulfilled
, rejected
) that handle the three states (pending
, fulfilled
, rejected
) of the Promise
returned by the side effect function load
:
pending
: Receives the current state state
as a parameter and sets the loading
flag to true
in the new state.fulfilled
: Receives the current state state
and the Promise fulfilled value items
as parameters, and sets items
to the parameter items
and loading
to false
in the new state.rejected
: Receives the current state state
and the Promise rejected error error
as parameters, and sets error
to the parameter error
and loading
to false
in the new state.How do we call the effects function in the component? The effects function will be merged into the actions object, so you can call the effects function through the actions object, as shown below:
In the example above, the three actions pending
, fulfilled
, and rejected
are generally required for HTTP requests used to obtain data. Reduck provides a utility function handleEffect
to simplify the creation of actions in this scenario.
For this type of side effect scenario, handleEffect
stipulates that the state structure of the model contains three fields: result
, error
, and pending
, with initial values of:
Calling handleEffect
will return the following data structure:
This data structure is the same as the data structure of the load
object under actions
. The object returned by handleEffect
actually corresponds to the three actions required by the Effects function.
We can use handleEffect
to rewrite todoModel
:
The handleEffect
function sets result
to items
in the received parameter object. Because todoModel
uses items
as the key to save the TODO list data instead of using the default result
as the key of handleEffect
, configuration is required here.
It is obvious that the todoModel
implemented through handleEffect
is much more concise than the previous implementation.
If you do not want all three states (pending, fulfilled, rejected) to be automatically handled by handleEffect
, for example, if the fulfilled state requires more complex data processing and needs to be manually handled, but you still want to automate the handling of the pending and rejected states, you can use the following method:
handleEffect
API.
In the Effects function, you can also manually call Actions. For example:
You can use the use
function to load other Models (including the Model itself) to achieve Model communication.
In some cases, we only need to read State and execute relevant side effect logic, and the side effect will not modify State.
For example, store some State in localStorage
:
or send message to server:
Sometimes, we want to execute subsequent logic directly based on the execution result of the side effect function. In this case, we need to use the return value of the Effects function.
For example, when the send button is clicked and the data is successfully sent, close the current popup immediately; if it fails, display an error message. We can achieve this through the following code: