zoukankan      html  css  js  c++  java
  • redux入门学习

    Redux学习

    redux 的工作流是单向数据流

    react 组件相当于借书者

    action相当于图书管理员

    store相当于图书馆

    reducers是图书管理软件

    react-action-store-reducers-store-react

    步骤

    1.用react脚手架创建一个新的项目

    2.visual studio code安装simple react snippet 插件

    3.然后再src目录下新建一个文件,TodoList.jsx

    在使用到snippet的地方 , 用快捷键ccc来生成react代码片段

    4.安装antd组件制作UI界面

    npm install antd --save-dev
    

    5.使用antd制作UI界面

    编辑文件(TodoList.jsx),如下代码,这样,基本的列表框就已经实现了(TodoList.jsx)

    import React, { Component } from "react";
    import 'antd/dist/antd.css';
    import { Input, Button, List } from "antd";
    
    const data = [
        '早8点开晨会',
        '早10点开晨会1',
        '早12点开晨会2'
    ]
    
    class TodoList extends Component {
        render() {
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder="write something" style={{  '250px', marginRight: '10px' }}></Input>
                        <Button type="primary">增加</Button>
                    </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={data}
                            renderItem={item => <List.Item>{item}</List.Item>}
                        />
    
                    </div>
                </div>
            );
        }
    }
    
    export default TodoList;
    

    6.安装redux

    npm install redux --save-dev
    

    7.创建redux中的仓库-store和reducer

    在src目录新建文件夹store,在store目录下面文件新建index.js文件,代码如下,该文件用于新建仓库

    import { createStore } from 'redux'
    //建立仓库
    const store = createStore();
    
    export default store;
    

    在store目录下新建reducer.js文件,该文件类似管理员的角色,代码如下

    const defaultState = {};
    
    export default (state = defaultState, action) => {
        return state;
    }
    

    最后修改index.js文件,将reducer引入到仓库里面,index.js文件代码如下,这样我们就将reducer和仓库建立了联系。

    import { createStore } from 'redux'
    import reducer from './reducer'
    
    
    //建立仓库
    const store = createStore(reducer);
    
    export default store;
    

    8.如何将TodoList里面的功能用redux实现呢?

    首先将初始值存储到reducer.js文件,代码如下:

    const defaultState = {
        inputValue: 'Write something',
        list: [
            '早8点开晨会',
            '早10点开晨会1',
            '早12点开晨会2'
        ]
    };
    
    export default (state = defaultState, action) => {
        return state;
    }
    

    然后在TodoList.js文件里面删除初始值,引入store,如下代码,刷新页面发现也可以正常显示了

    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);
            console.log(store.getState())
            this.state=store.getState();
        }
    
        render() {
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder={this.state.inputValue}
                        style={{  '250px', marginRight: '10px' }}></Input>
                        <Button type="primary">增加</Button>
                    </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={this.state.list}
                            renderItem={item => <List.Item>{item}</List.Item>}
                        />
    
                    </div>
                </div>
            );
        }
    }
    
    export default TodoList;
    
    

    安装google浏览器插件Redux DevTools,但是发现redux工具还使用不了

    去网页上可以https://github.com/zalmoxisus/redux-devtools-extension#usage 可以看到使用redux的基本过程,修改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;
    

    这样就可以在浏览器中使用redux dev tools工具了。

    9.通过input体验Redux的流程

    通过给input 添加动态变化事件来体验redux的过程,如下代码,给input添加onChange事件,然后通过dipatch将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);
            console.log(store.getState())
            this.state = store.getState();
            this.changeInputValue = this.changeInputValue.bind(this);
        }
    
        changeInputValue(e) {
            console.log(e.target.value);
            const action = {
                type: 'changeInput',
                value: e.target.value
            }
            store.dispatch(action);
        }
    
        render() {
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder={this.state.inputValue}
                            style={{  '250px', marginRight: '10px' }}
                            onChange={this.changeInputValue}
                        ></Input>
                        <Button type="primary">增加</Button>
                    </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={this.state.list}
                            renderItem={item => <List.Item>{item}</List.Item>}
                        />
    
                    </div>
                </div>
            );
        }
    }
    
    export default TodoList;
    
    

    在reducer.js文件中打印出action,就会发现action的内容如下,

    const defaultState = {
        inputValue: 'Write something',
        list: [
            '早8点开晨会',
            '早10点开晨会1',
            '早12点开晨会2'
        ]
    };
    
    export default (state = defaultState, action) => {
        console.log(action);
        //{type: "changeInput", value: "ggggfffffffffffffffffff"}
        //reducer里面只能接受state,不能改变state
        return state;
    }
    

    当每次改变文本框的值时,就会通知到reducer这边,action的内容就会被传递到这里。

    reducer里面只能接受state,不能改变state,如果想要修改state里面内容呢,index.js代码如下:

    刷新页面,可以看到state里面的变量值已经发生了变化。

    const defaultState = {
        inputValue: 'Write something',
        list: [
            '早8点开晨会',
            '早10点开晨会1',
            '早12点开晨会2'
        ]
    };
    
    export default (state = defaultState, action) => {
        console.log(state, action);
        //reducer里面只能接受state,不能改变state
        if (action.type == 'changeInput') {
            //
            let newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        }
        return state;
    }
    

    当时这里有个问题就是当设置了input的value值,必须要有一个redux的订阅动作,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);
            console.log(store.getState())
            this.state = store.getState();
            this.changeInputValue = this.changeInputValue.bind(this);
            this.storeChange = this.storeChange.bind(this);
        }
    
        changeInputValue(e) {
            console.log(e.target.value);
            const action = {
                type: 'changeInput',
                value: e.target.value
            }
            store.dispatch(action);
            store.subscribe(this.storeChange);
        }
    
        storeChange() {
            this.setState(store.getState())
        }
    
        render() {
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder={this.state.inputValue}
                            style={{  '250px', marginRight: '10px' }}
                            onChange={this.changeInputValue}
                            value={this.state.inputValue}
                        ></Input>
                        <Button type="primary">增加</Button>
                    </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={this.state.list}
                            renderItem={item => <List.Item>{item}</List.Item>}
                        />
    
                    </div>
                </div>
            );
        }
    }
    
    export default TodoList;
    
    

    10.通过TodoList列表体验Redux的流程

    给按钮添加功能函数,当增加按钮被点击时,下面的list组件就会发生变化,

    TodoList.js文件代码如下所示:通过store.dispatch(action) 函数发送给reducers。

    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);
            console.log(store.getState())
            this.state = store.getState();
            this.changeInputValue = this.changeInputValue.bind(this);
            this.storeChange = this.storeChange.bind(this);
            this.clickBtn = this.clickBtn.bind(this);
        }
    
        changeInputValue(e) {
            console.log(e.target.value);
            const action = {
                type: 'changeInput',
                value: e.target.value
            }
            store.dispatch(action);
            store.subscribe(this.storeChange);
        }
    
        storeChange() {
            this.setState(store.getState())
        }
    
        clickBtn() {
            const action = { type: 'addItem' }
            store.dispatch(action);
        }
    
        render() {
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder={this.state.inputValue}
                            style={{  '250px', marginRight: '10px' }}
                            onChange={this.changeInputValue}
                            value={this.state.inputValue}
                        ></Input>
                        <Button type="primary" onClick={this.clickBtn}>增加</Button>
                    </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={this.state.list}
                            renderItem={item => <List.Item>{item}</List.Item>}
                        />
    
                    </div>
                </div>
            );
        }
    }
    
    export default TodoList;
    
    

    相应地,Reducer.js 文件内容也要添加这个action相关的内容 ,当识别到增加按钮发来的action时,将state状态更新。代码如下所示:

    const defaultState = {
        inputValue: 'Write something',
        list: [
            '早8点开晨会',
            '早10点开晨会1',
            '早12点开晨会2'
        ]
    };
    
    export default (state = defaultState, action) => {
        console.log(state, action);
        //reducer里面只能接受state,不能改变state
        if (action.type === 'changeInput') {
            //
            let newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        }
    
        if (action.type === 'addItem') {
            let newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState;
        }
        return state;
    }
    

    当点击添加按钮之后,input文本框的内容置空,列表框也会增加内容了。这是因为TodoList.js文件中,已经订阅过相关内容,store.subscribe(this.storeChange) 当store里面的state内容 发生变化时,界面也会相应发生变化。如果将订阅的代码去掉,则界面不会发生变化。

    11.用Redux实现TodoList的删除功能

    如下代码,实现删除功能,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);
            console.log(store.getState())
            this.state = store.getState();
            this.changeInputValue = this.changeInputValue.bind(this);
            this.storeChange = this.storeChange.bind(this);
            this.clickBtn = this.clickBtn.bind(this);
            store.subscribe(this.storeChange);
        }
    
        changeInputValue(e) {
            console.log(e.target.value);
            const action = {
                type: 'changeInput',
                value: e.target.value
            }
            store.dispatch(action);
        }
    
        storeChange() {
            this.setState(store.getState())
        }
    
        clickBtn() {
            const action = { type: 'addItem' }
            store.dispatch(action);
        }
    
        deleteItem(index) {
            const action = {
                type: 'deleteItem',
                index
            };
            console.log("index is" + index);
            store.dispatch(action);
        }
    
        render() {
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder={this.state.inputValue}
                            style={{  '250px', marginRight: '10px' }}
                            onChange={this.changeInputValue}
                            value={this.state.inputValue}
                        ></Input>
                        <Button type="primary" onClick={this.clickBtn}>增加</Button>&nbsp;&nbsp;
                        <Button type="primary" onClick={this.deleteItem}>删除</Button>
                    </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={this.state.list}
                            renderItem={(item, index) => <List.Item onClick={this.deleteItem.bind(this, index)}>{item}</List.Item>}
                        />
                    </div>
                </div>
            );
        }
    }
    
    export default TodoList;
    
    

    相应的,reducer.js里面也要添加相应的内容,如下代码

    const defaultState = {
        inputValue: 'Write something',
        list: [
            '早8点开晨会',
            '早10点开晨会1',
            '早12点开晨会2'
        ]
    };
    
    export default (state = defaultState, action) => {
        console.log(state);
        console.log(action);
        
        //reducer里面只能接受state,不能改变state
        if (action.type === 'changeInput') {
            //
            let newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        }
    
        if (action.type === 'addItem') {
            let newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState;
        }
    
        if(action.type==='deleteItem') {
            let newState = JSON.parse(JSON.stringify(state));
            console.log(newState);
            newState.list.splice(action.index,1);
            return newState;
        }
        return state;
    }
    

    12.Redux的技巧

    看 上面的例子可以发现每次给reducer里面处理action的类型,如果写错了类型会很麻烦,不好定位,而且代码看起来也很乱。如何处理呢?下面我们通过在store目录下新建actionTypes.js文件,将action相关的内容用常量来表示,注意常量要大写哦。actionTypes.js代码如下:

    export const CHANGE_INPUT = 'changeInput'
    export const ADD_ITEM = 'addItem'
    export const DELETE_ITEM = 'deleteItem'
    

    接着我们将TodoList.js文件引入actionTypes.js,文件中涉及到的相应字符串进行替换为常量。

    reducer.js文件也是类似的。

    还有一个问题就是可以整理一下action相关的内容,在store目录下面新建actionCreatore.js文件,代码如下:

    import { CHANGE_INPUT, ADD_ITEM, DELETE_ITEM } from './actionTypes';
    
    export const changeInputAction = (value) => ({
        type: CHANGE_INPUT,
        value
    })
    
    export const addItemAction = () => ({
        type: ADD_ITEM
    })
    
    export const deleteItemAction = (index) => ({
        type: DELETE_ITEM,
        index
    })
    
    

    修改TodoList.js文件内容,使用actionCreators封装好的方法,代码如下:

    import React, { Component } from "react";
    import 'antd/dist/antd.css';
    import { Input, Button, List } from "antd";
    import store from './store/'
    import { changeInputAction, addItemAction, deleteItemAction } from './store/actionCreators'
    
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
            console.log(store.getState())
            this.state = store.getState();
            this.changeInputValue = this.changeInputValue.bind(this);
            this.storeChange = this.storeChange.bind(this);
            this.clickBtn = this.clickBtn.bind(this);
            store.subscribe(this.storeChange);
        }
    
        changeInputValue(e) {
            console.log(e.target.value);
            const action = changeInputAction(e.target.value);
            store.dispatch(action);
        }
    
        storeChange() {
            this.setState(store.getState())
        }
    
        clickBtn() {
            const action = addItemAction();
            store.dispatch(action);
        }
    
        deleteItem(index) {
            const action = deleteItemAction(index);
            console.log("index is" + index);
            store.dispatch(action);
        }
    
        render() {
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder={this.state.inputValue}
                            style={{  '250px', marginRight: '10px' }}
                            onChange={this.changeInputValue}
                            value={this.state.inputValue}
                        ></Input>
                        <Button type="primary" onClick={this.clickBtn}>增加</Button>&nbsp;&nbsp;
                    </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={this.state.list}
                            renderItem={(item, index) => <List.Item onClick={this.deleteItem.bind(this, index)}>{item}</List.Item>}
                        />
                    </div>
                </div>
            );
        }
    }
    
    export default TodoList;
    
    

    13.Redux常遇到的坑

    1.store唯一性:多个store是坚决不允许,只能有一个store空间

    2.只有store能改变自己的内容,reducer不能改变

    3.reducer必须是纯函数*,纯函数,函数返回的结果由参数输入值 决定。

    reducer里面不能调用ajax请求,因为服务器传递回来的数据可能不是固定的。

    14.组件UI和业务逻辑的拆分

    上面的代码里面,UI和业务逻辑其实还是融合在一起的。怎么解耦呢?

    1.在src目录下面,新建文件TodoListUI.js,专门放组件的UI部分,利用快速生成的方式imrc生成import,将TodoList里面的render函数直接剪切到TodoListUI.js文件里面,如下代码。

    import React, { Component } from 'react';
    import 'antd/dist/antd.css';
    import { Input, Button, List } from "antd";
    
    class TodoListUI extends Component {
        render() { 
            return ( 
                <div>
                <div style={{ margin: '2rem' }}>
                    <Input placeholder={this.state.inputValue}
                        style={{  '250px', marginRight: '10px' }}
                        onChange={this.changeInputValue}
                        value={this.state.inputValue}
                    ></Input>
                    <Button type="primary" onClick={this.clickBtn}>增加</Button>&nbsp;&nbsp;
                </div>
                <div style={{ margin: '10px',  '300px' }}>
                    <List
                        bordered
                        dataSource={this.state.list}
                        renderItem={(item, index) => <List.Item onClick={this.deleteItem.bind(this, index)}>{item}</List.Item>}
                    />
                </div>
            </div>
            );
        }
    }
     
    export default TodoListUI;
    

    2.在TodoList.js的render函数中引入TodoListUI组件,代码如下:

    import React, { Component } from 'react';
    import 'antd/dist/antd.css';
    import { Input, Button, List } from "antd";
    
    class TodoListUI extends Component {
    
        render() {
            console.log(this.props);
            return (
                <div>
                    <div style={{ margin: '2rem' }}>
                        <Input placeholder={this.props.inputValue}
                            style={{  '250px', marginRight: '10px' }}
                            onChange={this.props.changeInputValue}
                            value={this.props.inputValue}
                        ></Input>
                        <Button type="primary" onClick={this.props.clickBtn}>增加</Button>&nbsp;&nbsp;
                </div>
                    <div style={{ margin: '10px',  '300px' }}>
                        <List
                            bordered
                            dataSource={this.props.list}
                            renderItem={(item, index) =>
                                <List.Item onClick={(index) => { this.props.deleteItem(index) }}>{item}</List.Item>
                            }
                        />
                    </div>
                </div>
            );
        }
    }
    
    export default TodoListUI;
    

    TodoList.js组件代码如下:

    import React, { Component } from "react";
    import 'antd/dist/antd.css';
    import store from './store/'
    import { changeInputAction, addItemAction, deleteItemAction } from './store/actionCreators'
    import TodoListUI from './TodoListUI';
    
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
            console.log(store.getState())
            this.state = store.getState();
            this.changeInputValue = this.changeInputValue.bind(this);
            this.storeChange = this.storeChange.bind(this);
            this.clickBtn = this.clickBtn.bind(this);
            store.subscribe(this.storeChange);
            this.deleteItem = this.deleteItem.bind(this);
        }
    
        changeInputValue(e) {
            console.log(e.target.value);
            const action = changeInputAction(e.target.value);
            store.dispatch(action);
        }
    
        storeChange() {
            this.setState(store.getState())
        }
    
        clickBtn() {
            const action = addItemAction();
            store.dispatch(action);
        }
    
        deleteItem(index) {
            const action = deleteItemAction(index);
            console.log("index is" + index);
            store.dispatch(action);
        }
    
        render() {
            return (
                <TodoListUI
                    inputValue={this.state.inputValue}
                    changeInputValue={this.changeInputValue}
                    clickBtn={this.clickBtn}
                    list={this.state.list}
                    deleteItem={this.deleteItem}
                />
            );
        }
    }
    
    export default TodoList;
    
    

    3.运行上述的代码,会发现功能会实现,但是删除的时候删错了,是因为List.Item里面的点击函数,没有使用到renderItem传入的index,而是自己定义的index,其实没有用到renderItem传入的index。

     <List
                            bordered
                            dataSource={this.props.list}
                            renderItem={(item, index) =>
                                <List.Item onClick={(index) => { this.props.deleteItem(index) }}>{item}</List.Item>
    

    修改成下面的代码,功能就正常了。

      <List
                            bordered
                            dataSource={this.props.list}
                            renderItem={(item, index) =>
                                <List.Item onClick={() => { this.props.deleteItem(index) }}>{item}</List.Item>
                            }
                        />
    

    15.Redux中的无状态组件

    将TodoListUI.js改造成无状态组件,无状态组件就是一个函数了,不用继承component,使用jsx必须引入React。无状态组件的性能要比有状态组件高一些,代码如下:

    import React from 'react';
    import 'antd/dist/antd.css';
    import { Input, Button, List } from "antd";
    
    // class TodoListUI extends Component {
    
    //     render() {
    //         console.log(props);
    //         return (
    //             <div>
    //                 <div style={{ margin: '2rem' }}>
    //                     <Input placeholder={props.inputValue}
    //                         style={{  '250px', marginRight: '10px' }}
    //                         onChange={props.changeInputValue}
    //                         value={props.inputValue}
    //                     ></Input>
    //                     <Button type="primary" onClick={props.clickBtn}>增加</Button>&nbsp;&nbsp;
    //             </div>
    //                 <div style={{ margin: '10px',  '300px' }}>
    //                     <List
    //                         bordered
    //                         dataSource={props.list}
    //                         renderItem={(item, index) =>
    //                             <List.Item onClick={() => { props.deleteItem(index) }}>{item}</List.Item>
    //                         }
    //                     />
    //                 </div>
    //             </div>
    //         );
    //     }
    // }
    
    const TodoListUI = (props) => {
        return (
            <div>
                <div style={{ margin: '2rem' }}>
                    <Input placeholder={props.inputValue}
                        style={{  '250px', marginRight: '10px' }}
                        onChange={props.changeInputValue}
                        value={props.inputValue}
                    ></Input>
                    <Button type="primary" onClick={props.clickBtn}>增加</Button>&nbsp;&nbsp;
                </div>
                <div style={{ margin: '10px',  '300px' }}>
                    <List
                        bordered
                        dataSource={props.list}
                        renderItem={(item, index) =>
                            <List.Item onClick={() => { props.deleteItem(index) }}>{item}</List.Item>
                        }
                    />
                </div>
            </div>
        );
    }
    
    export default TodoListUI;
    

    16.Axios异步获取数据并和Redux结合

    先安装Axios,使用下面的命令

    npm install axios --save-dev
    

    在TodoList.js里面引入axios,然后在生命周期函数里面添加axios的相关东西,从axios里面获取数据,然后调用reducer里面的函数,给state中的list赋值 。

    17.Redux-thunk中间件的安装和配置

    redex-thunk中间件,常将调用接口的操作放到redux-thunk中去。

    安装

    npm install redux-thunk --save-dev
    

    接下来是配置redux-thunk

    首先是在store/index.js中引入,有个问题是redux-thunk引入的时候,之前的redux-dev插件就不能使用了,为了解决这个问题,引入增强函数,修改store/index.js的内容,代码如下:

    import { createStore, applyMiddleware, compose } from 'redux'
    import reducer from './reducer'
    import thunk from 'redux-thunk'
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
        window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
    
    const enhancer = composeEnhancers(applyMiddleware(thunk))
    
    //建立仓库
    const store = createStore(reducer, enhancer);
    
    export default store;
    

    18.Redux-saga中间件的安装和配置

    npm install react-saga --save-dev
    

    Redux-saga和react-thunk类似。这里先不详细介绍了

    19.React-Redux的介绍和安装

    安装react-redux插件,可以简化redux流程。

    新建一个脚手架项目,只保留index.js文件,

    create-react-app react-redux-project
    

    安装react-redux插件

    npm install react-redux --save-dev
    

    安装redux插件

    npm install redux --save-dev
    

    在src目录下,新建TodoList.js文件,代码如下:

    import React, { Component } from 'react';
    import store from './store'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
            this.state = store.getState();
        }
        render() {
            return (
                <div>
                    <div>
                        <input value={this.state.inputValue}></input>
                        <button>提交</button>
                    </div>
                    <ul>
                        <li>fff</li>
                    </ul>
                </div>
            );
        }
    }
    
    export default TodoList;
    

    在src新建store目录,在store目录下新建文件index.js文件和reducer.js文件,index.js文件代码如下:

    import { createStore } from 'redux'
    import reducer from './reducers'
    
    const store = createStore(reducer)
    export default store;
    

    reducers.js文件代码如下:

    const defaultState = {
        inputValue: 'jjj',
        list: []
    }
    
    export default (state = defaultState, action) => {
        return state;
    }
    

    刷新页面,此时页面可以成功地获取到state里面的内容了。

    接下来是react-redux插件的内容了。

    19.1react-redux的Provider和connect

    修改src目录下的index.js文件内容,只要被Provider包围的组件,都可以获得store里面的内容,代码如下,通过将store用Provider包围TodoList组件,TodoList组件就可以获得store里面的内容了。这样如果其他组件也想用到store的内容,直接放到Provider里面就可以了。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import TodoList from './TodoList';
    import { Provider } from 'react-redux';
    import store from './store'
    
    const App = (
      <Provider store={store}>
        <TodoList />
      </Provider>
    )
    
    ReactDOM.render(App,
    document.getElementById('root')
    );
    
    

    将TodoList组件用Provider包裹起来后,要在TodoList.js文件里面使用connect连接这个Provider中的内容,修改代码如下,通过将state变量映射成为props属性,在TodoList.js文件中(通过 this.props.inputValue)可以获取store里面存储的值。

    import React, { Component } from 'react';
    import { connect } from 'react-redux'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                <div>
                    <div>
                        <input value={this.props.inputValue}></input>
                        <button>提交</button>
                    </div>
                    <ul>
                        <li>fff</li>
                    </ul>
                </div>
            );
        }
    }
    
    const stateToProps = (state) => {
        console.log(state);
        //{inputValue: "jjj", list: Array(0)}
        return {
            inputValue: state.inputValue
        }
    }
    
    // export default TodoList; 
    export default connect(stateToProps, null)(TodoList);
    

    19.2 修改store和state

    首先给文本框添加onChange函数,TodoList.js代码如下:

    import React, { Component } from 'react';
    import { connect } from 'react-redux'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
        }
    
        changeValue(e) {
            console.log(e.target.value);
        }    
    
        render() {
            return (
                <div>
                    <div>
                        <input 
                            value={this.props.inputValue} 
                            onChange={this.changeValue.bind(this)}></input>
                        <button>提交</button>
                    </div>
                    <ul>
                        <li>fff</li>
                    </ul>
                </div>
            );
        }
    }
    
    const stateToProps = (state) => {
        console.log(state);
        //{inputValue: "jjj", list: Array(0)}
        return {
            inputValue: state.inputValue
        }
    }
    
    // export default TodoList; 
    export default connect(stateToProps, null)(TodoList);
    

    接下来通过连接器将onChange的内容传递到store里面,修改store中的状态值。将changeValue方法放到dispatchToProps方法中,文本框的绑定事件也可以用this.props.changeValue访问了,代码如下:

    import React, { Component } from 'react';
    import { connect } from 'react-redux'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                <div>
                    <div>
                        <input 
                            value={this.props.inputValue} 
                            onChange={this.props.changeValue}></input>
                        <button>提交</button>
                    </div>
                    <ul>
                        <li>fff</li>
                    </ul>
                </div>
            );
        }
    }
    
    const dispatchToProps = (dispatch) => {
        return {
            changeValue(e) {
                console.log(e.target.value);
            }    
        }
    }
    
    const stateToProps = (state) => {
        console.log(state);
        //{inputValue: "jjj", list: Array(0)}
        return {
            inputValue: state.inputValue
        }
    }
    
    // export default TodoList; 
    export default connect(stateToProps, dispatchToProps)(TodoList);
    

    一切正常了,最后我们需要将派发的函数修改一下,传到reducer中去,和之前一样。TodoList.js代码如下:

    import React, { Component } from 'react';
    import { connect } from 'react-redux'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                <div>
                    <div>
                        <input
                            value={this.props.inputValue}
                            onChange={this.props.changeValue}></input>
                        <button>提交</button>
                    </div>
                    <ul>
                        <li>fff</li>
                    </ul>
                </div>
            );
        }
    }
    
    const dispatchToProps = (dispatch) => {
        return {
            changeValue(e) {
                console.log(e.target.value);
                let action = {
                    type: 'Change_input',
                    value: e.target.value
                }
                //通过回调函数中的dispatch可以访问到store中的dispatch
                dispatch(action);
            }
        }
    }
    
    const stateToProps = (state) => {
        console.log(state);
        //{inputValue: "jjj", list: Array(0)}
        return {
            inputValue: state.inputValue
        }
    }
    
    // export default TodoList; 
    export default connect(stateToProps, dispatchToProps)(TodoList);
    

    reducer.js文件代码如下:

    const defaultState = {
        inputValue: 'jjj',
        list: []
    }
    
    export default (state = defaultState, action) => {
        if (action.type === 'Change_input') {
            let newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        }
        return state;
    }
    

    这样文本框就可以输入值并且发生变化了。

    19.4 增加和删除List数据

    接下来是增加和删除List中的数据,TodoList.js文件代码如下:

    import React, { Component } from 'react';
    import { connect } from 'react-redux'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                <div>
                    <div>
                        <input
                            value={this.props.inputValue}
                            onChange={this.props.changeValue}></input>
                        <button onClick={this.props.clickButton}>提交</button>
                    </div>
                    <ul>
                        {
                            this.props.list.map((value, index) => {
                                return (<li 
                                    key={index} 
                                    onClick={()=>{this.props.clickDelete(index)}}>{value}</li>)
                            })
                        }
                        
                    </ul>
                </div>
            );
        }
    }
    
    const dispatchToProps = (dispatch) => {
        return {
            changeValue(e) {
                console.log(e.target.value);
                let action = {
                    type: 'Change_input',
                    value: e.target.value
                }
                dispatch(action);
            },
            clickButton() {
                let action = {
                    type: 'Add_item'
                }
                dispatch(action);
            },
            clickDelete(index) {
                console.log(index);
                let action = {
                    type: 'Delete_item',
                    index
                }
                dispatch(action);
            }
        }
    }
    
    const stateToProps = (state) => {
        console.log(state);
        //{inputValue: "jjj", list: Array(0)}
        return {
            inputValue: state.inputValue,
            list: state.list
        }
    }
    
    // export default TodoList; 
    export default connect(stateToProps, dispatchToProps)(TodoList);
    

    reducer.js代码如下:

    const defaultState = {
        inputValue: 'jjj',
        list: []
    }
    
    export default (state = defaultState, action) => {
        if (action.type === 'Change_input') {
            let newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        }
        if (action.type === 'Add_item') {
            let newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState;
        }
        if (action.type === 'Delete_item') {
            let newState = JSON.parse(JSON.stringify(state));
            newState.list.splice(newState.index,1);
            return newState;
        }
        return state;
    }
    

    19.5代码优化

    解构赋值和无状态组件(只有render函数的组件可以优化为无状态组件),TodoList.js代码优化如下。

    import React from 'react';
    import { connect } from 'react-redux'
    
    
    const TodoList = (props) => {
        let { inputValue, changeValue, clickButton, list, clickDelete } = props;
        return (
            <div>
                <div>
                    <input
                        value={inputValue}
                        onChange={changeValue}></input>
                    <button onClick={clickButton}>提交</button>
                </div>
                <ul>
                    {
                        list.map((value, index) => {
                            return (<li
                                key={index}
                                onClick={() => { clickDelete(index) }}>{value}</li>)
                        })
                    }
                </ul>
            </div>
        );
    }
    // class TodoList extends Component {
    //     render() {
    //         let {inputValue,changeValue,clickButton,list,clickDelete}=this.props;
    //         return (
    //             <div>
    //                 <div>
    //                     <input
    //                         value={inputValue}
    //                         onChange={changeValue}></input>
    //                     <button onClick={clickButton}>提交</button>
    //                 </div>
    //                 <ul>
    //                     {
    //                         list.map((value, index) => {
    //                             return (<li 
    //                                 key={index} 
    //                                 onClick={()=>{clickDelete(index)}}>{value}</li>)
    //                         })
    //                     }
                        
    //                 </ul>
    //             </div>
    //         );
    //     }
    // }
    
    const dispatchToProps = (dispatch) => {
        return {
            changeValue(e) {
                console.log(e.target.value);
                let action = {
                    type: 'Change_input',
                    value: e.target.value
                }
                dispatch(action);
            },
            clickButton() {
                let action = {
                    type: 'Add_item'
                }
                dispatch(action);
            },
            clickDelete(index) {
                console.log(index);
                let action = {
                    type: 'Delete_item',
                    index
                }
                dispatch(action);
            }
        }
    }
    
    const stateToProps = (state) => {
        console.log(state);
        //{inputValue: "jjj", list: Array(0)}
        return {
            inputValue: state.inputValue,
            list: state.list
        }
    }
    
    // export default TodoList; 
    export default connect(stateToProps, dispatchToProps)(TodoList);
    

    通过上面的代码,可以发现通过react-redux,可以很容易将一个状态组件变成一个无状态组件,提高效率。大型项目可以通过stateToProps和dispatchToProps拆分成n个文件,方便多个人进行开发。

  • 相关阅读:
    Codeforces 1037D【BFS】
    hdu 4725 The Shortest Path in Nya Graph 【拆点】+【最短路】
    LightOJ 1074
    POJ1062昂贵的聘礼(经典) 枚举区间 +【Dijkstra】
    POJ 3159 Candies 【差分约束+Dijkstra】
    POJ 3126 Prime Path【BFS】
    ZOJ 1940 Dungeon Master【三维BFS】
    POJ 3279 Fliptile (二进制枚举)
    HDU 2612 find a way 【双BFS】
    POJ 3414 Pot (输出路径)【BFS】
  • 原文地址:https://www.cnblogs.com/zdjBlog/p/12614342.html
Copyright © 2011-2022 走看看