笔记内容转载自 AcWing 的 Web 应用课讲义,课程链接:AcWing Web 应用课。
之前我们提到过一个问题,就是如果两个兄弟组件要访问对方的数据,需要将数据存放到最近公共祖先上,这样当 DOM 树很复杂时就很麻烦。Redux 就是在整个 DOM 树之外拿出一个地方,用来存储一些全局的值。
1. Redux基本概念
Redux 会将 state
维护成一个树结构,每个节点存一个值,使用一个函数 reducer
维护每个值。Redux 用字典存储子节点,一般被称为 store
。
我们如果想修改树里面的某一个值,会将整个树重新计算一遍。我们会使用 dispatch
函数,他会递归调用每个 reducer
函数。此外还需要传入一个对象参数,表示需要对哪个节点进行操作,其中有个属性 type
,我们会给每个节点定义一个唯一的 type
。
Redux 的基本概念总结如下:
-
store
:存储树结构。 -
state
:维护的数据,一般维护成树的结构。 -
reducer
:对state
进行更新的函数,每个state
绑定一个reducer
。传入两个参数:当前state
和action
,返回新state
。 -
action
:一个普通对象,存储reducer
的传入参数,一般描述对state
的更新类型,其中的type
属性表示要修改的节点。 -
dispatch
:传入一个参数action
,对整棵state
树操作一遍,即递归调用整棵树的所有reducer
函数。
我们先创建一个 Redux 项目 redux-app
:
create-react-app redux-app
进入项目根目录,安装相关的模块:
npm i redux react-redux @reduxjs/toolkit
假设现在我们只维护一个 state
,我们构建一个最简单的朴素版 Redux(和 React 无关):
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { configureStore } from '@reduxjs/toolkit';
const f1 = (state = 0, action) => { // reducer函数
switch(action.type) {
case 'add':
return state + action.val;
case 'sub':
return state - action.val;
default:
return state;
}
};
const store = configureStore({ // 将f1函数构建成一棵状态树
reducer: f1
});
store.subscribe(() => {console.log(store.getState())}); // 每次dispatch完之后会执行一遍该函数
store.dispatch({type: 'add', val: 2}); // 修改state
store.dispatch({type: 'add', val: 3});
console.log(store.getState()); // 返回整棵树的值
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
</React.StrictMode>
);
现在来看看如何维护两个节点:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { configureStore } from '@reduxjs/toolkit';
const f1 = (state = 0, action) => { // reducer函数
switch(action.type) {
case 'add':
return state + action.val;
case 'sub':
return state - action.val;
default:
return state;
}
};
const f2 = (state = '', action) => {
switch(action.type) {
case 'concat':
return state + action.character;
default:
return state;
}
};
const f_all = (state = {}, action) => { // 组合了f1与f2
return {
f1: f1(state.f1, action),
f2: f2(state.f2, action),
}
};
const store = configureStore({ // 将f_all函数构建成一棵状态树
reducer: f_all
});
store.subscribe(() => {console.log(store.getState())}); // 每次dispatch完之后会执行一遍该函数
store.dispatch({type: 'add', val: 2}); // 修改f1的state
store.dispatch({type: 'add', val: 3});
store.dispatch({type: 'concat', character: 'abc'}); // 修改f2的state
console.log(store.getState()); // 返回整棵树的值
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
</React.StrictMode>
);
控制台输出如下:
{f1: 2, f2: ''}
{f1: 5, f2: ''}
{f1: 5, f2: 'abc'}
{f1: 5, f2: 'abc'}
f_all
也可以不用自己手写实现,可以使用 combineReducers
实现:
import { combineReducers } from '@reduxjs/toolkit';
const f_all = combineReducers({
f1: f1,
f2: f2,
});
2. React-Redux基本概念
现在我们来看一下 Redux 如何与 React 组合起来。我们需要用 Provider
将我们的整个项目包含起来。React-Redux基本概念如下:
-
Provider
组件:用来包裹整个项目,其store
属性用来存储 Redux 的store
对象。 -
connect(mapStateToProps, mapDispatchToProps)
函数:用来将store
与组件关联起来,该函数会返回一个函数,返回的函数可以将组件作为输入参数,然后返回一个新的组件,这个新的组件会将state
的值绑定到组件的props
属性上。-
mapStateToProps
:每次store
中的状态更新后调用一次,用来更新组件中的值,即将store
中state
的值绑定到组件的props
属性上。 -
mapDispatchToProps
:组件创建时调用一次,用来将store
的dispatch
函数传入组件,即将dispatch
函数映射到组件的props
属性上。
-
为了方便展示,我们定义三个组件:App
、Number
(state
从0开始)、String
(state
从空串开始)。
App
代码如下:
import React, { Component } from 'react';
import Number from './number';
import String from './string';
class App extends Component {
state = { }
render() {
return (
<React.Fragment>
<Number />
<hr />
<String />
</React.Fragment>
);
}
}
export default App;
然后我们在 index.js
中实现 React-Redux:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { configureStore } from '@reduxjs/toolkit';
import { combineReducers } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import App from './components/app';
const f1 = (state = 0, action) => { // reducer函数
switch(action.type) {
case 'add':
return state + action.val;
case 'sub':
return state - action.val;
default:
return state;
}
};
const f2 = (state = '', action) => {
switch(action.type) {
case 'concat':
return state + action.character;
default:
return state;
}
};
const f_all = combineReducers({
number: f1,
string: f2,
});
const store = configureStore({ // 将f_all函数构建成一棵状态树
reducer: f_all
});
store.subscribe(() => {console.log(store.getState())}); // 每次dispatch完之后会执行一遍该函数
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
现在我们来看一下如何在 Number
和 String
组件中调用他们的 state
值,需要用到一个 API:connect
,以 Number
为例:文章来源:https://www.toymoban.com/news/detail-707831.html
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Number extends Component {
state = { }
render() {
console.log(this.props);
return (
<React.Fragment>
<h3>Number: {this.props.number}</h3>
</React.Fragment>
);
}
};
const mapStateToProps = (state, props) => { // 第一个参数state包含整个状态树的树结构
return {
number: state.number,
}
};
export default connect(mapStateToProps)(Number);
现在我们再来看一下如何修改 state
的值,需要定义 mapDispatchToProps
对象将 dispatch
映射到 props
里,假设我们要在 Number
中操作 String
里的 state
:文章来源地址https://www.toymoban.com/news/detail-707831.html
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Number extends Component {
state = { }
handleClick = () => {
this.props.concat('abc');
}
render() {
console.log(this.props);
return (
<React.Fragment>
<h3>Number: {this.props.number}</h3>
<button onClick={this.handleClick}>添加</button>
</React.Fragment>
);
}
};
const mapStateToProps = (state, props) => { // 第一个参数state包含整个状态树的树结构
return {
number: state.number,
}
};
const mapDispatchToProps = {
concat: (character) => {
return { // 会返回一个对象,这个对象就是dispatch中用到的action,会将返回值作用到整个状态树中
type: 'concat',
character: character,
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Number);
到了这里,关于Web学习笔记-React(Redux)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!