redux学习总结与应用
redux重要知识点
首先,我先给出我学习react框架的参考教程,以下内容都是我依据此教程的个人总结。
redux中文教程
redux 是 JavaScript 状态容器,提供可预测化的状态管理。redux与react之间本身并无直接关系,但是它俩可以完美的搭配使用。简单来说,在流行的MVC三层架构理念中,react主要负责View层数据渲染,那么redux就主要负责Model层的数据存储与一些状态管理。
redux安装:
npm install --save redux
很多时候也许还要安装redux相关的库:
npm install --save react-redux
npm install --save-dev redux-devtools
Redux的三大原则
首先介绍redux的三大原则,其每一条原则都对应于下面的每一个核心:
- 单一数据源: 简单来说就是在整个应用中只允许有一个store。
- State 是只读的: 即state不允许被更改,若想产生新的state也只能通过action触发。
- 使用纯函数来执行修改 这里的修改是指产生新的state,而纯函数就是指reducer。
也许大家还并不清楚store,action以及reducer究竟是什么,没关系,带着三大原则接着往下看。
Store
store就是redux的数据容器,存储着当前项目里的所有数据,所以有且只有一个。在与react框架结合使用时,store通常是与react框架的*父组件绑定在一起。
Talk is cheap,show you the code:
// 创建store
let store = createStore(todoApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
还有几个和store相关的api,这里我先不作讲解,后面内容有用到时再进行讲述。
Action
action是把数据从应用(如果是与react框架结合使用,那么这里的应用可以值react的component组件)传到 store 的有效载荷。它是 store 数据的唯一来源,一般来说是通过 store.dispatch() 将 action 传到 store。
action 本质上是 Object,按照约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。不同的action的type代表不同的状态,所以state的更新只能由action触发。
下面就是四种不同的action实例:
let nextTodoId = 0;
const TODO_ADD = 'TODO_ADD';
export const actionAddTodo = (todo: IToDo) => ({
type: TODO_ADD,
id: nextTodoId++,
todo
});
const TODO_EDIT = 'TODO_EDIT';
export const actionEditTodo = (id: number, todo: IToDo) => ({
type: TODO_EDIT,
id,
todo
});
const TODO_DELETE = 'TODO_DELETE';
export const actionDeleteTodo = (id: number) => ({
type: TODO_DELETE,
id
});
const TODO_STATUS = 'TODO_STATUS';
export const actionSetStatusType = (id: number, status: ToDoStatus) => ({
type: TODO_STATUS,
id,
status
});
Reducer
reducer是干啥的,很简单,就是将action所响应的改变发送给store。reducer里具体实现了如何根据action的状态改变来更新state。
Talk is cheap,show you the example:
const todoList = (state: IReduxState['todoList'] = [], action: { type: string; } & any) => {
switch (action.type) {
case 'TODO_ADD':
return [
...state,
{
id: action.id,
title: action.todo.title,
text: action.todo.text,
createdTime: String(new Date()),
expiredTime: action.todo.expiredTime,
emailAddress: action.todo.emailAddress,
status: ToDoStatus.New,
}
]
case 'TODO_EDIT':
return state.map(todo =>
(todo.id === action.id)
? {
id: todo.id,
title: action.todo.title,
text: action.todo.text,
createdTime: todo.createdTime,
expiredTime: action.todo.expiredTime,
emailAddress: action.todo.emailAddress,
status: action.todo.status,
}
: todo
)
case 'TODO_DELETE':
return state.filter(todo => todo.id != action.id)
case 'TODO_STATUS':
return state.map(todo =>
(todo.id === action.id)
? { ...todo, status: action.status }
: todo
)
default:
return state
}
}
我这里给出的一个完整的reducer是完全相对应上面的action。
我怕有朋友会疑惑,这里我说明一个东西:
文中所说的state既是reducer的参数,也是store里的部分数据。而我所说的action的状态是指在reducer里根据不同的action.type来执行不同的更新state的逻辑。
这里再补充一个store与reducer的关系:store的数据储层结构与reducer一一对应。
这是我项目里的store:
export type IReduxState = {
todoList: Array<IToDo>;
filterStatus: FilterStatus;
pageStatus: PageStatus;
id: number;
}
这是我项目里的最外层reducer:
const todoApp = combineReducers({
todoList,
filterStatus,
pageStatus,
id,
});
每一个store分支都有对应的reducer,所以所有的state的修改都是在reducer内进行。
Redux的三大核心之间的关系
我以addTodo的操作流程为示例来讲述store,action以及reducer三者之间的关系。
首先由外界发出一个dispatch(actionAddTodo(todo))来告知store要更新数据(注意,此action是携带有todo这个数据),然后reducer根据此action的type知道是addTodo所以就执行’TODO_ADD’的逻辑来更改store里的数据。
react与redux配合使用的流程原理
我们知道react的最核心部分就是组件和每个组件里面的参数props。宏观来看,react根据redux数据render出页面,当要更新数据时,react通知redux更新自己的数据,然后因为redux数据的改变,react所render出的页面也会发生变化。
在redux教程中有一个完整的react与redux结合使用的小案例,大家可以参看着学习。
下面我仍以addTodo功能的实现来讲解react与redux的结合使用。
(我给出的案例功能更强大并且使用的是typescipt语言)
addTodo页面:
import * as React from 'react'
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { actionSetPageType, actionAddTodo } from '../actions/actions';
import { PageStatus, ToDoStatus } from '../interfaces/interfaces';
type AddProps = {
dispatch?: Dispatch;
}
@(connect() as ClassDecorator)
export default class AddTodo extends React.Component<AddProps> {
state = {
title: '',
text: '',
expiredTime: '',
emailAddress: ''
}
private jumpToAppPage = () => {
this.props.dispatch(actionSetPageType(PageStatus.App));
}
private changeValue = (e: React.ChangeEvent<HTMLInputElement>) => {
switch (e.target.name) {
case 'title':
this.setState({ title: e.target.value });
break;
case 'text':
this.setState({ text: e.target.value });
break;
case 'expiredTime':
this.setState({ expiredTime: e.target.value });
break;
case 'emailAddress':
this.setState({ emailAddress: e.target.value });
break;
default:
break;
}
}
private createTodo = () => {
const todo = {
id: 0,
title: this.state.title,
text: this.state.text,
createdTime: '0000',
expiredTime: this.state.expiredTime,
emailAddress: this.state.emailAddress,
status: ToDoStatus.New
};
this.props.dispatch(actionAddTodo(todo));
this.jumpToAppPage();
}
render() {
return (
<div>
<h3>Add</h3>
<form action="javascript:void(0)" onSubmit={this.createTodo}>
<label htmlFor="">Title</label>
<input type="text" value={this.state.title} name='title' onChange={this.changeValue} />
<br />
<label htmlFor="">Text</label>
<input type="text" value={this.state.text} name='text' onChange={this.changeValue} />
<br />
<label htmlFor="">ExpiredTime</label>
<input type="text" value={this.state.expiredTime} name='expiredTime' onChange={this.changeValue} />
<br />
<label htmlFor="">EmailAddress</label>
<input type="text" value={this.state.emailAddress} name='emailAddress' onChange={this.changeValue} />
<br />
<input type='submit' value='Create' />
</form>
<a href="javascript:void(0)" onClick={this.jumpToAppPage}>Back to List</a>
</div>
)
}
}
此页面由react组件所构成,当页面触发form表单的onSubmit事件时,store会发送dispatch一个action来告知要更新数据。
然后紧接着进行上述的三大关系的流程直至更新store数据,最后由于store数据更新,react所render的页面也会变化。
todoList页面代码:
import * as React from 'react'
import { IReduxState, FilterStatus, ToDoStatus, PageStatus } from '../interfaces/interfaces';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { actionSetPageType, actionDeleteTodo, actionJumpPageId } from '../actions/actions';
const mapStateToProps = (state: IReduxState) => {
return {
todoList: state.todoList,
status: state.filterStatus
}
}
type TodoListProps = {
todoList?: IReduxState['todoList'];
status?: FilterStatus;
dispatch?: Dispatch;
};
@(connect(mapStateToProps) as ClassDecorator)
export default class TodoList extends React.Component<TodoListProps> {
private getVisibleTodoS = () => {
switch (this.props.status) {
case FilterStatus.New:
return this.props.todoList.filter(todo => todo.status === ToDoStatus.New);
case FilterStatus.Done:
return this.props.todoList.filter(todo => todo.status === ToDoStatus.Done);
case FilterStatus.Expired:
return this.props.todoList.filter(todo => todo.status === ToDoStatus.Expired);
case FilterStatus.All:
default:
return this.props.todoList;
}
}
private jumpToEditPage = (id: number) => {
this.props.dispatch(actionJumpPageId(id));
this.props.dispatch(actionSetPageType(PageStatus.Edit));
}
private deleteHandler = (id: number) => {
const result = window.confirm('Are you sure?');
if (result) {
this.props.dispatch(actionDeleteTodo(id));
}
}
render() {
const todoList = this.getVisibleTodoS();
return (
<table>
<thead>
<th>Id</th>
<th>Title</th>
<th>Text</th>
<th>CreatedTime</th>
<th>ExpiredTime</th>
<th>EmailAddress</th>
<th>Status</th>
<th></th>
</thead>
<tbody>
{todoList.map(todo => (
<tr>{
Object.values(todo).map(value =>//TODO status
<td>{value}</td>
)
}
<td>
<a href="javascript:void(0)" onClick={() => {
return this.jumpToEditPage(todo.id);
}}>Edit</a>
{" | "}
<a href="javascript:void(0)" onClick={() => {
return this.deleteHandler(todo.id);
}}>Delete</a>
</td>
</tr>
))}
</tbody>
</table >
)
}
}
结语
其实只要我们弄清楚了react以及redux分别的作用领域,那么将它俩结合在一起使用也就不难。
如果有对此知识点不懂的地方欢迎评论区留言,我看到消息后会及时答复。
最后,由于此项目代码比较多,完整的代码我放在了github上,有兴趣的朋友可以自行下载使用。
在bash命令行中输入下列代码即可运行项目:
npm run dev
源码链接:https://github.com/rookieery/TodoListApp
本文地址:https://blog.csdn.net/asd0356/article/details/107596557