Redux入门

1.Redux概念简述

Redux设计理念

  • Redux设计理念如上图,Redux要求把数据放到公共存储区域Store里,组件中尽量少放数据。当蓝色组件修改公共数据时,Store中的值发生改变,其他使用到该数据的组件到Store中取修改后的值即可。

  • Redux = Reducer+Flux

    Flux原本是官方提供的辅助React的数据层框架,但公共数据存储区域由于有很多Store而出现问题,故最终升级成Redux。

2.Redux工作流程

Redux工作流程
* ReactComponents:每个组件(理解:借书的人)
* Store:存储数据的公共区域(理解:图书管理员)
* Action Creators:组件想获取的数据(理解:说的要借的书的话)
* Reducers:Store查询的内容(理解:管理员查看书的记录本)
* 用于理解的工作流程:可以看做是你到图书馆借书,你是借书的人[React Components],你说了一句你想借什么书[Action Creators]告诉图书管理员[Store],图书管理员查看他的记录本[Reducers],并返回给你你要的书。

3.使用Ant Design(React的UI框架)实现ToList页面布局

  • 安装Ant Design:yarn add antdnpm 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组件的引用可参见上方代码,通过属性增改对应内容

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函数接收的回调函数就会被执行
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值