redux入门学习

摘要:
目录Redux学习1.用react脚手架创建一个新的项目2.visualstudiocode安装simplereactsnippet插件3.然后再src目录下新建一个文件,TodoList.jsx4.安装antd组件制作UI界面5.使用antd制作UI界面6.安装redux7.创建redux中的仓库-store和reducer8.如何将TodoList里面的功能用redux实现呢?import{createStore}from'redux'importreducerfrom'./reducer'//建立仓库conststore=createStore;exportdefaultstore;8.如何将TodoList里面的功能用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个文件,方便多个人进行开发。

免责声明:文章转载自《redux入门学习》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Android学习笔记之 SimpleAdapter 中添加按钮响应事件,getView的重写动态创建线程(多线程)处理大量数据下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Egret入门学习日记 --- 第十二篇(书中 5.1节 内容)

 第十二篇(书中 5.1节 内容)   昨天把 第4章完成了。   今天来看第5章。   接下来是 5.1节 的内容。                      总结一下 5.1节 的重点:     1、如何制作一个公用按钮皮肤。   跟着做:     重点1:如何制作一个公用按钮皮肤。       首先,先创建一个 exml 文件。           ...

react 的虚拟dom

前端优化的主要方面就是减少页面的DOM操作,减少重排和重绘,React在这方面做了优化,采用了所谓的虚拟DOM,其实我们平时也会遇到虚拟DOM,只是你没有注意罢了,请听我娓娓道来。  所谓的虚拟DOM就是JavaScript对象,就是在没有真实渲染DOM之前做的操作,给你举几个例子来看看:  (1)createElement('Button')  这就创建...

句柄类与继承

前一小节《容器与继承》http://blog.csdn.net/thefutureisour/article/details/7744790提到过: 对于容器,假设定义为基类类型,那么则不能通过容器訪问派生类新增的成员;假设定义为派生类类型,一般不能用它承载基类的对象,即使利用类型转化强行承载,则基类对象能够訪问没有意义的派生类成员,这样做是非常危...

Android系统--输入系统(十一)Reader线程_简单处理

Android系统--输入系统(十一)Reader线程_简单处理 1. 引入 Reader线程主要负责三件事情 获得输入事件 简单处理 上传给Dispatch线程 InputReader.cpp void InputReader::loopOnce() { ...... size_t count = mEventHub->ge...

HTML 表单常用的代码元素

表单: 将数据通过浏览器提交到服务器的媒介。<form action="" method="get/post" ></form> get 提交有长度限制 post 提交无长度限制 一般常用post  表单元素:12个 1.文本类1).<input type="text" value=""> - 文本框 在文本框中输入...

简单基于OPENGL的三维CAD框架(1)工具类

在vc++中有CDC类,同样也可以开发基于OPENGL的OPenGLDC类,这样可以像调用CDC类一样调用OPenGLDC类 首先给出两个工具类,点类和向量类 typedef struct tagVector3D {double dx;double dy;double dz;} VECTOR3D; class CVector3D : public VECT...