概述
1.Redux概念简述
Redux设计理念如上图,Redux要求把数据放到公共存储区域Store里,组件中尽量少放数据。当蓝色组件修改公共数据时,Store中的值发生改变,其他使用到该数据的组件到Store中取修改后的值即可。
Redux = Reducer+Flux
Flux原本是官方提供的辅助React的数据层框架,但公共数据存储区域由于有很多Store而出现问题,故最终升级成Redux。
2.Redux工作流程
* ReactComponents:每个组件(理解:借书的人)
* Store:存储数据的公共区域(理解:图书管理员)
* Action Creators:组件想获取的数据(理解:说的要借的书的话)
* Reducers:Store查询的内容(理解:管理员查看书的记录本)
* 用于理解的工作流程:可以看做是你到图书馆借书,你是借书的人[React Components],你说了一句你想借什么书[Action Creators]告诉图书管理员[Store],图书管理员查看他的记录本[Reducers],并返回给你你要的书。
3.使用Ant Design(React的UI框架)实现ToList页面布局
安装Ant Design:
yarn add antd
或npm install antd --save
使用Ant Design组件实例:
//Todolist.js import React ,{Component} from 'react'; import 'antd/dist/antd.css';//antd是全局变量 import {Input,Button,List} from 'antd'; const Names = [ 'Jack', 'Sam', 'Tom', 'Daming' ] class Todolist extends Component{ render(){ return( <div style={{marginTop:'10px',marginLeft:"10px"}}> <div> {/*输入框组件*/} <Input placeholder="todoInfo" style={{width:'300px',marginRight:'10px'}}/> {/*按钮组件*/} <Button type="primary">提交</Button> {/*列表组件 header列表头 footer列表底 bordered列表有边框 dataSource列表渲染的数据资源 renderItem怎么渲染,dataSource中的每一个数据都会调用renderItem中的函数进行渲染,将每一条数据渲染成List.Item的组件,数据的内容就是dataSource中每一项数据的内容 */} <List header={<div>header</div>} footer={<div>footer</div>} bordered dataSource={Names} renderItem={item=>(<List.Item>{item}</List.Item>)} style={{marginTop:'10px',width:'300px'}} /> </div> </div> ) } } export default Todolist;
- 使用时需要引入Ant Design的css样式【ant是全局变量】:
import 'antd/dist/antd.css';
- 使用对应组建时可以直接到Ant Design首页查看组件引入代码并使用:https://ant.design/docs/react/introduce-cn
- 简单Input、Button、List组件的引用可参见上方代码,通过属性增改对应内容
- 使用时需要引入Ant Design的css样式【ant是全局变量】:
4.创建Redux中的store
- 安装redux:
yarn add redux
- (为了结构整齐,创建store文件夹,并新建index.js)在index.js中引入redux中的createStore方法,创建store的时候
const store = createStore(reducer);
需要将reducer(在store文件下创建reducer.js)传进来[可以理解为:创建管理员的时候要把小本子给他]。reducer(负责管理应用里的数据),返回的是一个函数,两个参数,第一个参数state是数据,action表示操作。使用时可以使用this.getState()
获取store中的数据。 完整代码:
//【store/reducer.js】 const defaultState = { //当前store中有inputValue和list两条数据 inputValue:'123', list:[1,2] } //reducer是笔记本,笔记本中存放关于图书馆书籍的数据和对数据的操作内容,state即是整个图书馆里书籍的信息(Store中的数据) export default (state=defaultState,action)=>{ return state; } //【store/index.js】 import {createStore} from 'redux' import reducer from './reducer' //将笔记本引入进来 //创建数据的公共存储仓库 const store = createStore(reducer); export default store; //【Todolist.js】 import React ,{Component} from 'react'; import 'antd/dist/antd.css';//antd是全局变量 import {Input,Button,List} from 'antd'; import store from './store/index' //也可以直接写成 import store from './store' 会默认直接找index.js class Todolist extends Component{ constructor(props) { super(props); //console.log(store.getState()); //store的方法,用于获取store中的数据 this.state=store.getState(); console.log(this.state); } render(){ return( <div style={{marginTop:'10px',marginLeft:"10px"}}> <div> {/*输入框组件*/} <Input placeholder="todoInfo" style={{width:'300px',marginRight:'10px'}} value={this.state.inputValue}/> {/*按钮组件*/} <Button type="primary">提交</Button> {/*列表组件 header列表头 footer列表底 bordered列表有边框 dataSource列表渲染的数据资源 renderItem怎么渲染,dataSource中的每一个数据都会调用renderItem中的函数进行渲染,将每一条数据渲染成List.Item的组件,数据的内容就是dataSource中每一项数据的内容 */} <List header={<div>header</div>} footer={<div>footer</div>} bordered dataSource={this.state.list} renderItem={item=>(<List.Item>{item}</List.Item>)} style={{marginTop:'10px',width:'300px'}} /> </div> </div> ) } } export default Todolist;
5.Action和Reducer的编写
在Chrome网上应用店安装Redux DevTools(Chrome浏览器插件,用于调试Redux)
网页上使用开发者工具Redux调试需要在createStore第二个参数写上
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
。表示如果window有变量(Redux的开发者工具扩展)则执行对应方法(使用对应工具)。注意:reducer可以接收state,但是不能修改state(通常是将之前的state拷贝一份出来,改变拷贝的state,return改变后的state,return的数据返回给了store,store会拿新的数据替换老的数据,此时store中的数据就被替换了)。
实例代码:
//[store/reducer.js] const defaultState = { //当前store中有inputValue和list两条数据 inputValue:'123', list:[1,2] } //reducer是笔记本,笔记本中存放关于图书馆书籍的数据和对数据的操作内容,state即是整个图书馆里书籍的信息(Store中的数据) //reducer可以接受state,但是不能修改state export default (state=defaultState,action)=>{ console.log(state,action); //state表示上次的存储state,action表示用户传递过来的那句话 if(action.type === 'change_input_value'){ const newState = JSON.parse(JSON.stringify(state));//对之前的state做一个深拷贝 newState.inputValue = action.value; return newState; }else if(action.type === 'add_todo_item'){ const newState = JSON.parse(JSON.stringify(state)); newState.list = [...newState.list,newState.inputValue]; newState.inputValue=''; return newState; } return state; } //[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; //[Todolist.js] import React ,{Component} from 'react'; import 'antd/dist/antd.css';//antd是全局变量 import {Input,Button,List} from 'antd'; import store from './store/index' //也可以直接写成 import store from './store' 会默认直接找index.js class Todolist extends Component{ constructor(props) { super(props); //console.log(store.getState()); //store的方法,用于获取store中的数据 this.state=store.getState(); console.log(this.state); this.handleInputChange = this.handleInputChange.bind(this); this.handleStoreChange = this.handleStoreChange.bind(this); this.handleButtonClick = this.handleButtonClick.bind(this); store.subscribe(this.handleStoreChange);//将handleStoreChange函数订阅给store,每当store中的数据发生变化的时候将调用此函数 } render(){ return( <div style={{marginTop:'10px',marginLeft:"10px"}}> <div> {/*输入框组件*/} <Input placeholder="todoInfo" onChange={this.handleInputChange} style={{width:'300px',marginRight:'10px'}} value={this.state.inputValue}/> {/*按钮组件*/} <Button type="primary" onClick={this.handleButtonClick}>提交</Button> {/*列表组件 header列表头 footer列表底 bordered列表有边框 dataSource列表渲染的数据资源 renderItem怎么渲染,dataSource中的每一个数据都会调用renderItem中的函数进行渲染,将每一条数据渲染成List.Item的组件,数据的内容就是dataSource中每一项数据的内容 */} <List header={<div>header</div>} footer={<div>footer</div>} bordered dataSource={this.state.list} renderItem={item=>(<List.Item>{item}</List.Item>)} style={{marginTop:'10px',width:'300px'}} /> </div> </div> ) } handleInputChange(e){ //创建action const action = { type:'change_input_value',//描述要做的事情 value:e.target.value //表示改变inputvalue,变成e.target.value } store.dispatch(action); //将action传递给store } handleStoreChange(){ this.setState(store.getState());//当store发生变化时,则调用此方法。用store中的数据替换当前组件的state数据。 } handleButtonClick(){ const action={ type:'add_todo_item' } store.dispatch(action); } } export default Todolist;
6.使用Redux完成todolist的删除功能
新增代码内容
//[Todolist.js] import React ,{Component} from 'react'; import 'antd/dist/antd.css';//antd是全局变量 import {Input,Button,List} from 'antd'; import store from './store/index' //也可以直接写成 import store from './store' 会默认直接找index.js class Todolist extends Component{ constructor(props) { super(props); //console.log(store.getState()); //store的方法,用于获取store中的数据 this.state=store.getState(); console.log(this.state); this.handleInputChange = this.handleInputChange.bind(this); this.handleStoreChange = this.handleStoreChange.bind(this); this.handleButtonClick = this.handleButtonClick.bind(this); store.subscribe(this.handleStoreChange);//将handleStoreChange函数订阅给store,每当store中的数据发生变化的时候将调用此函数 } render(){ return( <div style={{marginTop:'10px',marginLeft:"10px"}}> <div> {/*输入框组件*/} <Input placeholder="todoInfo" onChange={this.handleInputChange} style={{width:'300px',marginRight:'10px'}} value={this.state.inputValue}/> {/*按钮组件*/} <Button type="primary" onClick={this.handleButtonClick}>提交</Button> {/*列表组件 header列表头 footer列表底 bordered列表有边框 dataSource列表渲染的数据资源 renderItem怎么渲染,dataSource中的每一个数据都会调用renderItem中的函数进行渲染,将每一条数据渲染成List.Item的组件,数据的内容就是dataSource中每一项数据的内容 */} <List header={<div>header</div>} footer={<div>footer</div>} bordered dataSource={this.state.list} renderItem={(item,index)=>(<List.Item onClick={this.handleItemDelete.bind(this,index)}>{item}</List.Item>)} style={{marginTop:'10px',width:'300px'}} /> </div> </div> ) } handleInputChange(e){ //创建action const action = { type:'change_input_value',//描述要做的事情 value:e.target.value //表示改变inputvalue,变成e.target.value } store.dispatch(action); //将action传递给store } handleStoreChange(){ this.setState(store.getState());//当store发生变化时,则调用此方法。用store中的数据替换当前组件的state数据。 } handleButtonClick(){ const action={ type:'add_todo_item' } store.dispatch(action); } handleItemDelete(index){ const action={ type:'delete_todo_item', index:index } store.dispatch(action); } } export default Todolist; //[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; //[store/reducer.js] const defaultState = { //当前store中有inputValue和list两条数据 inputValue:'', list:[] } //reducer是笔记本,笔记本中存放关于图书馆书籍的数据和对数据的操作内容,state即是整个图书馆里书籍的信息(Store中的数据) //reducer可以接受state,但是不能修改state export default (state=defaultState,action)=>{ console.log(state,action); //state表示上次的存储state,action表示用户传递过来的那句话 if(action.type === 'change_input_value'){ const newState = JSON.parse(JSON.stringify(state));//对之前的state做一个深拷贝 newState.inputValue = action.value; return newState; }else if(action.type === 'add_todo_item'){ const newState = JSON.parse(JSON.stringify(state)); newState.list = [...newState.list,newState.inputValue]; newState.inputValue=''; return newState; }else if(action.type === 'delete_todo_item'){ const newState = JSON.parse(JSON.stringify(state)); newState.list.splice(action.index,1); return newState; } return state; }
- 为
<List.item>
绑定点击的监听事件handleItemDelete(),传递下标index - 在handleItemDelete方法中创建action,并利用store的dispatch方法将action传递给store
- store将直接到reducer中查询,在reducer中添加匹配的type值判断并给出根据下标的splice方法返回新的state给store
- store将新的state返回给TodoList组件
- 由于Todolist组件利用store.subscribe()方法订阅了store,故会调用对应的handleStoreChange()方法,并在方法中利用store.getState()重新获取state值完成当前组件的state更新,进而重新渲染页面
- 为
7.ActionTypes的拆分
有时我们会将定义的action的type值与reducer中判断的type值写错,从而导致项目运行不起来,但却没有报错,故我们可以将type值提取出来成一个新的文件。
示例代码:[下述代码将只针对源码中的修改进行介绍]
//[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] //导入actionTypes.js文件 import {CHANGE_INPUT_VALUE,ADD_TODO_ITEM,DELETE_TODO_ITEM} from './store/actionTypes'; //使用常量值 handleInputChange(e){ //创建action const action = { type:CHANGE_INPUT_VALUE,//描述要做的事情 value:e.target.value //表示改变inputvalue,变成e.target.value } store.dispatch(action); //将action传递给store } handleButtonClick(){ const action={ type:ADD_TODO_ITEM, } store.dispatch(action); } handleItemDelete(index){ const action={ type:DELETE_TODO_ITEM, index:index } store.dispatch(action); } //[store/reducer.js] import {CHANGE_INPUT_VALUE,ADD_TODO_ITEM,DELETE_TODO_ITEM} from './actionTypes'; const defaultState = { //当前store中有inputValue和list两条数据 inputValue:'', list:[] } //reducer是笔记本,笔记本中存放关于图书馆书籍的数据和对数据的操作内容,state即是整个图书馆里书籍的信息(Store中的数据) //reducer可以接受state,但是不能修改state export default (state=defaultState,action)=>{ console.log(state,action); //state表示上次的存储state,action表示用户传递过来的那句话 if(action.type === CHANGE_INPUT_VALUE){ const newState = JSON.parse(JSON.stringify(state));//对之前的state做一个深拷贝 newState.inputValue = action.value; return newState; }else if(action.type === ADD_TODO_ITEM){ const newState = JSON.parse(JSON.stringify(state)); newState.list = [...newState.list,newState.inputValue]; newState.inputValue=''; return newState; }else if(action.type === DELETE_TODO_ITEM){ const newState = JSON.parse(JSON.stringify(state)); newState.list.splice(action.index,1); return newState; } return state; }
8.使用actionCreator统一创建action
将action的创建放到actionCreators.js进行统一管理,提高代码的可维护性
示例代码:[下述代码将只针对源码中的修改进行介绍]
//[store/actionCreators.js] import {CHANGE_INPUT_VALUE,ADD_TODO_ITEM,DELETE_TODO_ITEM} from './actionTypes'; export const getInputChangeAction = (value) => ({ type:CHANGE_INPUT_VALUE, value }) export const getAddItemAction = () => ({ type:ADD_TODO_ITEM }) export const getDeleteItemAction = (index) => ({ type:DELETE_TODO_ITEM, index }) //[Todolist.js] //引入actionCreators.js import {getInputChangeAction,getAddItemAction,getDeleteItemAction} from './store/actionCreators' //直接调用actionCreators.js中的方法创建action并传递给store handleInputChange(e){ //创建action const action = getInputChangeAction(e.target.value); store.dispatch(action); //将action传递给store } handleButtonClick(){ const action = getAddItemAction(); store.dispatch(action); } handleItemDelete(index){ const action=getDeleteItemAction(index); store.dispatch(action); }
9.Redux知识点复习补充
- Redux在设计和使用时有三个基本原则
- store是唯一的【上述代码中我们在store/index.js中定义,所有组件均使用这一个store】
- 只有store能够改变自己的内容【上述代码中,reducer拿到之前的数据并生成新的数据,将新的数据返回给store,store拿到新的数据并更新】
- Reducer必须是纯函数(什么是纯函数?给定固定的输入,就一定有固定的输出,而且不会有任何副作用——副作用指的是对参数的修改)【上述代码中reducer导出的是函数,且给定了具体的state和action,一定有固定的输出内容,且在函数内部不会修改参数的值】
- Redux中核心的API
- createStore:创建store
- store.dispatch:派发action给store
- store.getState:获得store中所有的数据内容
- store.subscribe:订阅store的改变,只要store发生改变,store.subscribe函数接收的回调函数就会被执行
最后
以上就是大方板栗为你收集整理的Redux入门的全部内容,希望文章能够帮你解决Redux入门所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复