惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 小笨笨

C# 面试题整理 《ASP.NET Core 6 框架揭秘》第五章读书笔记 - 配置选项(上) 《ASP.NET Core 6 框架揭秘》第四章读书笔记 - 文件系统 Entity Framework Core 笔记 - 入门 《ASP.NET Core 6 框架揭秘》第三章读书笔记 - 依赖注入(下) React 学习笔记之二 - React 详解 《Kafka 权威指南》之二 - 安装 Kafka 《Kafka 权威指南》读书笔记之一 - 初识Kafka React 学习笔记之一 - ES6 基础 微服务实战读书笔记-第一章 - 小笨笨 缠论 jQuery 实战读书笔记之第六章:事件本质 jQuery 实战读书笔记之第五章:使用 jQuery 操作页面 jQuery 实战读书笔记之第四章:使用特性、属性和数据 jQuery 实战读书笔记之第三章:操作 jQuery 集合 jQuery 实战读书笔记之第二章:选择元素 TCP/IP详解读书笔记:ARP-地址解析协议 TCP/IP详解读书笔记:链路层 TCP/IP详解读书笔记:概述 - 小笨笨
React 学习笔记之三 - 基于 Redux 状态管理
小笨笨 · 2022-11-29 · via 博客园 - 小笨笨

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

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。