Some common tools of Redux and linkage with React

In contact with the content of Redux, it is inevitable to contact some of its supporting tools, for a while by these tools to make some confusion, do not know which is which, their respective roles and connections. This article will simply record my query results.

First, let’s talk about these tools, redux, redux-thunk, redux-toolkit, react-redux.

  • ** Redux **: It is a core library with simple functions. It is just a simple Finite-State Machine, but the idea behind it is not simple. It is the legendary “one hundred lines of code, one thousand lines of documentation”.
  • ** React-Redux **: It is a connection library with’React ‘that notifies’React’ to update the component when’Redux 'status is updated.
  • ** Redux-Thunk **: Provide an asynchronous solution for’Redux ‘to make up for the lack of’Redux’ functionality.
  • ** Redux-Toolkit **: The Redux Toolkit comes with some useful packages such as Immer, Redux-Thunk, and Reselect. It makes the job easier for React developers, allowing them to directly change state (not handle immutability) and apply Middleware like Thunk (handle asynchronous operations). It also uses Reselect, a simple “selector” library from Redux, to simplify reducer functions.

That is to say, the relationship between these four, redux is the core, is a Finite-State Machine, the role of redux-thunk is to make this state function better handle asynchrony (not that redux can’t handle asynchrony), redux-toolkit It is a step further, making redux more concise and reliable to write, it contains redux-thunk, so if not with react, we can use react + react-toolkit

However, simple internal changes of Finite-State Machine cannot start the re-rendering of react, so we need a tool to help us do this, that is, react-redux.

Then let’s follow the order of redux, with redux-thunk, with redux-toolkit, and finally with react-redux to see how to write this code.

Redux

The first is the writing of redux, which can be directly read from my last blog.Redux入门笔记

Redux

This is an example in the official doc of redux-thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

//enter thunk Middleware when createStore
const store = createStore(rootReducer, applyMiddleware(thunk));

Method for initiating network requests
function fetchSecretSauce() {
return fetch('https://www.baidu.com/s?wd=Secret%20Sauce');
}

//The following two are normal actions
function makeASandwich(forPerson, secretSauce) {
return {
type: 'MAKE_SANDWICH',
forPerson,
secretSauce,
};
}

function apologize(fromPerson, toPerson, error) {
return {
type: 'APOLOGIZE',
fromPerson,
toPerson,
error,
};
}

//This is an asynchronous action, request the network first, make ASandwich on success, apologize on failure
function makeASandwichWithSecretSauce(forPerson) {
return function (dispatch) {
return fetchSecretSauce().then(
(sauce) => dispatch(makeASandwich(forPerson, sauce)),
(error) => dispatch(apologize('The Sandwich Shop', forPerson, error)),
);
};
}

//The final dispatch is an asynchronous action makeASandwichWithSecretSauce
store.dispatch(makeASandwichWithSecretSauce('Me'));

That is to say, with the addition of redux-thunk Middleware, you can dispatch a function whose parameter is dispatch. Inside this function, you can do some asynchronous operations, and then really dispatch after the asynchronous success.

At the same time, the source code of this redux-thunk is actually very simple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action = 'function') {
return action(dispatch, getState, extraArgument);
}

return next(action);
};
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

Redux

Since Redux-Toolkit already integrates Redux-Thunk, there is no need to use Redux-Thunk alone.

React and Redux are considered the best combination for managing state in large-scale React applications. However, over time, the popularity of Redux has declined due to:

  • Configuring the Redux Store is not easy.
    We need several packages to make Redux work with React.
  • Redux requires too much boilerplate code.

The Redux Toolkit comes with some useful packages such as Immer, Redux-Thunk, and Reselect.

Immer can make the job easier for React developers, allowing them to change state directly (without dealing with immutability).

  • Middleware like Thunk can be used to handle asynchronous operations.

  • It also uses Reselect, a simple “selector” library from Redux, to simplify the reducer function.

Create

Create a file named src/app/store.js. Import the configureStore API from Redux Toolkit. We’ll start by creating an empty Redux store, and exporting it:

1
2
3
4
5
6
7
//app/store.js

import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
reducer: {},
})

This creates a Redux store, and also automatically configure the Redux DevTools extension so that you can inspect the store while developing.

Provide

Once the store is created, we can make it available to our React components by putting a React-Redux <Provider> around our application in src/index.js. Import the Redux store we just created, put a <Provider> around your <App>, and pass the store as a prop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './app/store'
import { Provider } from 'react-redux'

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)

Create

Add a new file named src/features/counter/counterSlice.js. In that file, import the createSlice API from Redux Toolkit.

Creating a slice requires a string name to identify the slice, an initial state value, and one or more reducer functions to define how the state can be updated. Once a slice is created, we can export the generated Redux action creators and the reducer function for the whole slice.

Redux requires that we write all state updates immutably, by making copies of data and updating the copies. However, Redux Toolkit’s createSlice and createReducer APIs use Immer inside to allow us to write “mutating” update logic that becomes correct immutable updates.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//features/counter/counterSlice.js
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It

// doesn't actually mutate the state because it uses the Immer library,

// which detects changes to a "draft state" and produces a brand new

// immutable state based off those changes
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer

Add

Next, we need to import the reducer function from the counter slice and add it to our store. By defining a field inside the reducers parameter, we tell the store to use this slice reducer function to handle all updates to that state.

1
2
3
4
5
6
7
8
9
//app/store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export default configureStore({
reducer: {
counter: counterReducer,
},
})

Redux

Use

Now we can use the React-Redux hooks to let React components interact with the Redux store. We can read data from the store with useSelector, and dispatch actions using useDispatch. Create a src/features/counter/Counter.js file with a <Counter> component inside, then import that component into App.js and render it inside of <App>.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//features/counter/Counter.js

import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
const count = useSelector((state) => state.counter.value)
const dispatch = useDispatch()

return (
<div>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
\>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
\>
Decrement
</button>
</div>
</div>
)
}

Connect

This is a typical HOC way of separating container components from display components, which was very popular before hooks

You can see the details here: https://react-redux.js.org/tutorials/connect

Reference link:

https://juejin.cn/post/6869950884231675912

https://segmentfault.com/a/1190000039806379

https://redux-toolkit.js.org/introduction/getting-started