Redux从入门到进阶,看这一篇就够了!
Posted 星期一研究室
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redux从入门到进阶,看这一篇就够了!相关的知识,希望对你有一定的参考价值。
Redux,带你从入门到进阶
🌂序言
大家都知道, react
是单向数据流,所以它传递数据也较为简单,父子之间的关系也较为明确。但是呢,如果我们要做更多复杂数据的传递,单单使用 react
是完全不够的。因此,我们需要用到 redux
来做更为复杂的数据传递。
那在下面的这篇文章中,将从入门到进阶,讲解 redux
的工作流程。
叮!开始 redux
之旅吧~👏
☂️一、基础知识
1、Redux概念简述
对于 react
来说,它是一个非视图层的轻量级框架,如果要用它来传递数据的话,则要先父传子,然后再慢慢地一层一层往上传递。
但如果用 redux
的话,假设我们想要某个组件的数据,那这个组件的数据则会通过 redux
来存放到 store
中进行管理。之后呢,通过 store
,再来将数据一步步地往下面的组件进行传递。
值得注意的是,我们可以视 Redux
为 Reducer
和 Flux
的结合。
2、Redux的工作流程
Redux
,实际上就是一个数据层的框架,它把所有的数据都放在了 store
之中。我们先来看一张图:
大家可以看到中间的 store
,它里面就存放着所有的数据。继续看 store
向下的箭头,然后呢,每个组件都要向 store
里面去拿数据。
我们用一个例子来梳理整张图,具体如下:
- ①整张图上有一个
store
,它存放着所有的数据,也就是存储数据的公共区域; - ②每个组件,都要从
store
里面拿数据; - ③假设现在有一个场景,模拟我们要在图书馆里面借书。那么我们可以把
react Component
理解为借书人,之后呢,借书人要去找图书馆管理员才能借到这本书。而借书这个过程中数据的传递,就可以把它视为是Action Creators
,可以理解为 “你想要借什么书” 这句话。 - ④
Action Creatures
去到store
。这个时候我们把store
当做是图书馆管理员,但是,图书馆管理员是没有办法记住所有图书的数据情况的。一般来说,它都需要一个记录本,你想要借什么样的书,那么她就先查一下;又或者你想要还什么书,她也要查一下,需要放回什么位置上。 - ⑤这个时候就需要跟
reducers
去通信,我们可以把reducers
视为是一个记录本,图书馆管理员用这个记录本来记录需要的数据。管理员store
通过reducer
知道了应该给借书人Components
什么样的数据。
🎃二、使用Antd实现TodoList页面布局
1、在项目中使用Antd
打开 antdesign
的官网👉antd官网传送门,我们先来在项目中引入它。具体步骤如下:
第一步,安装 antd
。命令如下:
npm install antd --save
第二步,引入样式。代码如下:
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
2、使用Antd实现TodoList的基本布局
首先,我们在项目的 src
文件夹下创建一个新的文件,命名为 TodoList.js
。具体代码如下:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
class TodoList extends Component {
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<div>
<Input placeholder="todo info" style={{ width: '300px' }} />
<Button type="primary">提交</Button>
</div>
<List
bordered
dataSource={data}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
export default TodoList;
此时浏览器的显示效果为:
3、创建redux中的store
(1)创建store
接下来我们来创建项目中的 store
。首先在项目的 src
文件夹下创建一个新的文件夹,命名为 store
。接着,我们在 store
文件夹下面,创建一个新的文件,命名为 index.js
。具体代码如下:
import { createStore } from "redux";
import reducer from './reducer';
const store = createStore(reducer);
export default store;
然后呢,继续在 store
文件夹下面创建一个新的文件,命名为 reducer.js
。具体代码如下:
const defaultStore = {
inputValue: '',
list: []
};
export default (state = defaultStore, action) => {
return state;
}
由此,我们就创建完成了项目中的 store
。
(2)在项目中使用store
创建完 store
之后,我们先初步在项目中使用这个 store
,以便于看看效果。先来添加 store
中的数据,首先改造在 store
中的 reducer.js
文件,具体代码如下:
const defaultStore = {
inputValue: '123',
list: [1, 2]
};
export default (state = defaultStore, action) => {
return state;
}
之后改造 TodoList.js
。具体代码如下:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
}
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<div>
<Input placeholder={this.state.inputValue} style={{ width: '300px' }} />
<Button type="primary">提交</Button>
</div>
<List
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
export default TodoList;
此时浏览器的显示效果为:
🧵三、Action和Reducer的编写 - 增添功能
1、主体页面内容改造
接下来,我们使用 action
和 reducer
,来对这个组件的数据进行前后传递。首先,先来改造 TodoList.js
文件。具体代码如下:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
store.subscribe(this.handleStoreChange)
}
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<div>
<Input
value={this.state.inputValue}
placeholder="todo info"
style={{ width: '300px', marginRight: '10px'}}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleBtnClick}>提交</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
handleInputChange(e) {
// 在react中,action是一个对象的形式
// type旨在告诉react说,你帮我去改变input的值,这个值是下面的value,也就是e.target.value
const action = {
type: 'change_input_value',
value: e.target.value
}
store.dispatch(action)
// console.log(e.target.value)
}
handleStoreChange() {
// 当感知到store的数据发生变化时,那么就去调用store.getState方法,从store里面再重新取一次数据,
// 然后去调用setState,替换掉当前store里面的数据
this.setState(store.getState())
}
handleBtnClick() {
const action = {
type: 'add_todo_item'
}
store.dispatch(action)
}
}
export default TodoList;
接下来我们来分析以上代码。首先,每一个动作分别会先去绑定对应的事件,之后呢,在事件里面,去创造 action
。而对于创造的 action
来说,它旨在告诉 react
,让 react
去帮忙 action
去改变某个值,而这个值就是它绑定的 value
。
最后, action
要做的事情结束了,那么它的数据就需要去存储到 store
里面。于是通过 store.dispatch(action)
来进行处理,将 action
的数据传递到 store
里面。
2、改变action中的数据
对于 action
一开始的值来说,它是固定的。但有时候我们是想要去修改action中的值,这个时候就需要用到 reducer
。现在,我们来改造下 reducer.js
文件,让 input
框可以自由的输入值,同时,点击提交按钮之后,进行列表的增添操作。具体代码如下:
const defaultStore = {
inputValue: '123',
list: [1, 2]
};
// reducer 可以接收state,但是绝不能修改state
const reducer = (state = defaultStore, action) => {
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === 'add_todo_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
console.log(newState);
return newState;
}
return state;
}
export default reducer;
3、store数据改造
下面,我们来看下 store
文件夹下 index.js
的内容。我们需要对其进行简单的改造,具体代码如下:
import { createStore } from "redux";
import reducer from './reducer';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
除了 reducer
之外,我们还要将 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
给传递进去并调用这个方法。
最后,我们来看下浏览器的显示效果:
🧶四、使用Redux实现TodoList的删除功能
1、对组件进行事件绑定
上面我们实现了增添功能,那么现在,我们继续来实现删除功能,实现每点击每一项时,能够删除点击项的数据。先来在 TodoList.js
文件中绑定对应的事件,具体代码如下:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store';
class TodoList extends Component {
constructor(props) {
// 此处省略上述已有代码
}
render() {
return (
{/* 此处省略上述已有代码 */}
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick={this.handleItemDelete.bind(this, index)}>{item}</List.Item>}
/>
</div>
)
}
// 此处省略上述已有代码
handleItemDelete(index) {
const action = {
type: 'delete_todo_item',
index
}
store.dispatch(action);
}
}
export default TodoList;
2、在reducer中进行数据通信
接着,我们在 reducer.js
文件中,对数据进行通信。具体代码如下:
const defaultStore = {
inputValue: '123',
list: [1, 2]
};
// reducer 可以接收state,但是绝不能修改state
const reducer = (state = defaultStore, action) => {
// 此处省略上述已有代码
if (action.type === 'delete_todo_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState;
}
return state;
}
export default reducer;
现在,我们来看下浏览器的显示效果:
👓五、逻辑归纳
1、ActionTypes的拆分
在上面的 TodoList.js
中,大家可以看到,我们会频繁地去操作 action
。同时,假设说其中的 type
如果我们稍微写错了一个字母,那排错的过程总是不好定位的。
因此,我们要来做的一件事情就是 ActionTypes
的拆分。
首先,我们在 store
文件夹下新增一个文件,命名为 actionTypes.js
。具体代码如下:
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
其次,改造 TodoList.js
下的内容。具体代码如下:
import {
CHANGE_INPUT_VALUE,
ADD_TODO_ITEM,
DELETE_TODO_ITEM
} from './store/actionTypes'
class TodoList extends Component {
handleInputChange(e) {
const action = {
type: CHANGE_INPUT_VALUE,
value: e.target.value
}
store.dispatch(action)
}
handleStoreChange() {
this.setState(store.getState())
}
handleBtnClick() {
const action = {
type: ADD_TODO_ITEM
}
store.dispatch(action)
}
handleItemDelete(index) {
const action = {
type: DELETE_TODO_ITEM,
index
}
store.dispatch(action);
}
}
export default TodoList;
最后,改造 reducer.js
文件。具体代码如下:
import {
CHANGE_INPUT_VALUE,
ADD_TODO_ITEM,
DELETE_TODO_ITEM
} from './actionTypes';
const defaultStore = {
inputValue: '123',
list: [1, 2]
};
const reducer = (state = defaultStore, action) => {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === ADD_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
console.log(newState);
return newState;
}
if (action.type === DELETE_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState;
}
return state;
}
export default reducer;
通过将 change_input_value
、 add_todo_item
和 delete_todo_item
进行整合,将其整合到 actionTypes.js
文件下,这样,如果我们遇到字母写错的情况下,也能够更好的进行排错。
2、使用actionCreator统一创建action
在上面的 TodoList.js
中,大家可以看到,对于几个绑定的事件来说,我们总是要频繁的去创建 action
,重复性
以上是关于Redux从入门到进阶,看这一篇就够了!的主要内容,如果未能解决你的问题,请参考以下文章