Ideally React gives us a mental model of:
f(data) => view
This is fine if the data never changes. However, React exists to deal with data updates via props (immutable) and state (changes based on setState()
API). In the real world, data is shaped like a tree and view is shaped like a tree. They don't always match.
Facebook invented a the Flux pattern to solve this shared state issue. Redux is an implementation of the Flux architectural pattern. Redux expects the data to be a singleton state tree that listens for messages to manipulate the state as appropriate:
These are the React Components that consume the store as its data. There is a special way Redux will map its data from the state tree into the different React Components. The Components will know to re-render when these bits of state are changed.
Actions are messages that represent some event, such as a user's action or a network request. With the aid of reducers, they affect the overall state.
This is a singleton state tree. The state tree is immutable and needs to be re-created at every action. This helps connected views to know when to update itself - just doing a simple reference comparison rather than a deep comparison.
These are simple stateless, pure functions that takes state + action message and returns a copy of state some modifications according to the action message type and payload.
There is a single dispatcher. It simply informs of the store of all the actions that needs to be performed. Certain middleware can be applied to the store and the dispatcher's job is to dispatch the message through all the middleware layers.
Redux is used inside many large and complex applications because of its clarity and its predictability. It is really easy to debug and is easily extensible via its middleware architecture. In this exercise, we'll explore the heart of how Redux modifies state.
Redux uses what is called a "reducer" to modify its state. It is called this because a "reducer" is what is used inside an Array.reduce()
.
A reducer is a pure function that receives some state and an action message as inputs and generates a copy of modified state as the output. Redux manages state changes mainly through reducers, and they are directly related to the UI, so for this exercise, we'll have to use jest tests to see the inner workings.
From the official documentation site:
Reducers are just pure functions that take the previous state and an action, and return the next state. Remember to return new state objects, instead of mutating the previous state.
Mental Model: think of Reducer as part of the store and should have no side effects outside of defining how data can change from one state to next given action messages.
-
First, take a look at the store interface in the
exercise/src/store/index.tsx
- note that theStore
interface has two keys:todos
andfilter
. We'll concentrate on thetodos
which is an object where the keys are IDs and the values are of anTodoItem
type. -
Open
exercise/src/reducers/pureFunctions.ts
and fill in the missing body of the pure functions. -
Open
exercise/src/reducers/index.ts
and fill in the missing case statements for the switch ofaction.type
. -
Open
exercise/src/reducers/pureFunctions.spec.ts
and implement tests for the functions you wrote forremove
,complete
, andclear
.