追求艺术的脚步
Be the change you want to see in the world.Things are always as hard as you think but always as easy as you do.

组件嵌套多层时,可能需要把父级的状态一层一层向下传递,这样在管理和使用上极其不便。

Redux 是 JS 的状态容器,提供可预测化的状态管理。在 React 中使用 Redux,可以把所有的 state 集中到组件顶部,能灵活地将所有 state 分发给所有的组件。

 

3.1 Redux 使用

Redux 本身不依赖于任何库,可以与任何 UI 层框架搭配使用,大小也只有 2KB。

 

核心概念

  1)store:一个数据容器,用来管理和保存项目的 state,整个应用只能有一个 store。

  2)state:一个对象,在 state 中存储相应的数据,需要时通过 store 提供的方法获取。

  3)action:一个通知命令,视图层发起的一个操作,对 state 进行修改。

 

3.1.1 action、createStore 和 reducer 函数

action 本质上是一个 JS 普通对象,必须包含一个字符串类型的 type 属性,代表要对 state 做何种操作。最终 action 通过 store 传入 reducer 函数,完成对 state的修改。

Redux Toolkit 是官方推荐的用于编写 Redux 逻辑的方式,它包含了 Redux Core,还包含了对创建 Redux 应用非常必要的包和功能。

安装 Redux Core:npm install redux

安装 Redux Toolkit:npm install @reduxjs/toolkit

 

先看 Redux 中的核心方法 createStore,用于创建项目中的 store。

 1      /**
 2          * This is a reducer - a function that takes a current state value and an
 3          * action object describing "what happened", and returns a new state value.
 4          * A reducer's function signature is: (state, action) => newState
 5          *
 6          * The Redux state should contain only plain JS objects, arrays, and primitives.
 7          * The root state value is usually an object. It's important that you should
 8          * not mutate the state object, but return a new object if the state changes.
 9          *
10          * You can use any conditional logic you want in a reducer. In this example,
11          * we use a switch statement, but it's not required.
12          */
13         function counterReducer(state = { value: 0 }, action) {
14             switch (action.type) {
15                 case 'counter/incremented':
16                 return { value: state.value + 1 }
17                 case 'counter/decremented':
18                 return { value: state.value - 1 }
19                 default:
20                 return state
21             }
22         }
23 
24         // Create a Redux store holding the state of your app.
25         // Its API is { subscribe, dispatch, getState }.
26         let store = createStore(counterReducer)

createStore 创建 store 时需要传入 reducer 函数。reducer 是一个函数,它接受当前状态值和描述“发生了什么”的操作对象,并返回一个新的状态值。

在 Redux 中,所有的数据都会保存在同一个 state 对象中。

 

3.1.2 store

store 的三个常用方法。

  1)getState:用于获取 state。

  2)dispatch(action):发起一个 action。

  3)subscribe(listener):注册一个监听器监听 state 发生的变化。返回一个注销监听器的方法,用于取消监听器。

 

Redux 基本流程:基于 reducer 创建 store,从 store 中获取 state 传递给视图,当视图被操作时,通过 dispatch 发起一个 action,store 接到 action 后,把 state 和 action 传递给 reducer,reducer 更新 state 并把新的 state 传给视图进行更新。

下面是纯 Redux 的测试代码:

 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <title>React Demo</title>
 5         <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
 6         <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
 7         <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.0/redux.min.js" crossorigin></script> 
 8     </head>
 9     <body>
10         <div>
11             <p>
12                 <span id="colorEL">Watch my color.</span>    
13                 <button id="red">RED</button>
14                 <button id="green">GREEN</button>
15                 <button id="toggle">TOGGLE</button>            
16             </p>            
17         </div>
18         <script>
19             var initialState = { color: 'red' };
20 
21             // Reducer
22             function colorReducer(state, action){
23                 if(typeof state == 'undefined'){
24                     console.log('return initialState');
25                     return initialState;
26                 }
27 
28                 switch(action.type){
29                     case 'RED':
30                         return { color: 'red' };
31                     case 'GREEN':
32                         return { color: 'green' };
33                     case 'TOGGLE':
34                         return state.color == 'red' ? { color: 'green' } : { color: 'red' };
35                     default:
36                         return state;
37                 }
38             }
39 
40             // create store by the reducer
41             var myStore = Redux.createStore(colorReducer);
42             var colorEL = document.getElementById('colorEL');
43 
44             function render(){
45                 colorEL.style.color = myStore.getState().color;
46             }
47 
48             render();
49 
50             myStore.subscribe(render);
51 
52             document.getElementById('red').addEventListener('click', function(){
53                 myStore.dispatch({ type: 'RED' });
54             });
55 
56             document.getElementById('green').addEventListener('click', function(){
57                 myStore.dispatch({ type: 'GREEN' });
58             });
59 
60             document.getElementById('toggle').addEventListener('click', function(){
61                 myStore.dispatch({ type: 'TOGGLE' });
62             });
63         </script>  
64     </body>
65 </html>

 

 

3.2 React-Redux

在 React 中直接使用 Redux 特别麻烦,需要一层一层传递 store。

Redux 官方提供了一个 React 绑定库 - React-Redux。

 

3.2.1 安装与配置

命令:npm install react-redux,需要注意的是,React-Redux 是 Redux 的官方针对 React 开发的扩展库,默认没有在 React 项目中安装,需要手动来安装。React-Redux 是依赖于redux,所以 Redux 也 必须安装。

安装了 React-Redux 之后,就可以把数据和视图进行分离。

  1)第一步,创建 store。

  2)通过 React-Redux 把 store 关联到项目中。

    React-Redux 提供了组件 Provider,它的作用是向后代子孙传递信息。Provider 应该在整个项目的最外层。

    

3.2.2 connect

React-Redux 提供了 connect 方法用于接收 Provider 传递下来的 store 中的 state 和 dispatch。

下面是一个使用了 connect 的例子。

 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <title>React Demo</title>
 5         <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
 6         <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
 7         <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
 8         <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
 9         <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.0/redux.min.js" crossorigin></script>
10         <script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/8.0.5/react-redux.min.js" crossorigin></script>
11         <script src="https://unpkg.com/babel-standalone@6/babel.min.js" crossorigin></script>     
12     </head>
13     <body>
14         <div id="root"></div>
15         <script type="text/babel">
16             // Reducer
17             function countFn(state = {
18                 count: 1
19             }, action){
20                 switch(action.type){
21                     case "COUNT_PLUS":
22                         return { count: state.count + 1};
23                     case "COUNT_REDUCE":
24                         return { count: state.count - 1};
25                 }
26 
27                 return state;
28             }
29 
30             // create store by the reducer
31             let store = Redux.createStore(countFn);
32 
33             class App extends React.Component{
34                 render(){
35                     let {count, dispatch} = this.props;
36 
37                     return (
38                         <div>
39                             <button onClick={() => {
40                                 dispatch({type: "COUNT_REDUCE"});
41                             }}>-</button>
42                             <span>{count}</span>
43                             <button onClick={() => {
44                                 dispatch({type: "COUNT_PLUS"});
45                             }}>+</button>
46                         </div>
47                     );
48                 }
49             }
50 
51             App = ReactRedux.connect(state => state)(App);
52 
53             ReactDOM.createRoot(document.querySelector('#root'))
54                 .render(<ReactRedux.Provider store={store}>
55                             <App />
56                         </ReactRedux.Provider>);
57         </script>  
58     </body>
59 </html>

 

3.2.3 Hooks

React-Redux 7.X 新增了 Hooks。

  1)const state = useSelector(state => state)

  useSelector 接收一个回调函数,这个函数会被调用,并且 store 中的 state 会被传递给这个函数,返回值是想要的数据,必须有返回值。

  2)const dispatch = useDispatch()

  返回值为 Redux 的 dispatch 方法。

  3) const store = useStore()

  返回 Redux 的 store。

 

3.4 reducer 拆分与合并

combineReducers(rootReducer)。

 

3.5 redux-thunk 中间件

dispatch 一个 action 之后,到达 reducer 之前,进行一些额外的操作,就需要用到 middleware。你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。
换言之,中间件都是对 store.dispatch() 的增强。

redux-thunk 主要用于解决项目中的异步请求。

  1)安装:npm install redux-thunk。

  2)在创建 store 时,引入中间件。Redux 中提供了 applyMiddleware(...middlewares)方法,把方法的返回值传递给 createStore。

  3)添加完成后可以利用 thunk 做中间处理。使用了 thunk 之后,dispatch 可以接收两种不同类型的参数。参数类型是对象时,不会经过中间件处理而是直接把 action 发送到 reducer。参数类型是函数时,会把 dispatch 和 getState 作为参数传递给该函数,在函数中进行中间处理,处理之后调用 dispatch 更新 state。

posted on 2022-11-29 15:45  小笨笨  阅读(119)  评论(0编辑  收藏  举报