当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 前端笔记之React(五)Redux深入浅出

前端笔记之React(五)Redux深入浅出

2019年08月02日  | 移动技术网IT编程  | 我要评论
一、Redux整体感知 Redux是JavaScript状态管理容器,提供了可被预测状态的状态管理容器。来自于Flux思想,Facebook基于Flux思想,在2015年推出Redux库。 中文网站:http://www.redux.org.cn/ 官方git:https://github.com/ ...

一、redux整体感知

reduxjavascript状态管理容器,提供了可被预测状态的状态管理容器。来自于flux思想,facebook基于flux思想,在2015年推出redux库。

中文网站:

官方git

 

首先要引redux.js包,这个包提供了redux对象,这个对象可以调用redux.createstore()方法。

<body>
    <h1 id="info"></h1>
    <button id="btn1">加</button>
    <button id="btn2">减</button>
    <button id="btn3">乘</button>
    <button id="btn4">除</button>

    <script type="text/javascript" src="redux.min.js"></script>
    <script type="text/javascript">
        //这是一个 reducer,形式为 (state, action) => state 的纯函数。
        //纯函数:函数内部,不改变传入的参数,只return新值。
        //描述了 action 如何把 state 转变成下一个 state。
        const reducer = (state = {"v" : 10} , action)=>{
            if(action.type == "add"){
                return {"v" : state.v + 1};
            }else if(action.type == "minus"){
                return {"v" : state.v - 1};
            }else if(action.type == "cheng2"){
                return {"v" : state.v * 2}
            }
             return state;
        }
        //创建 redux store 来存放应用的状态。
        //store翻译为“仓库”,这是一个存放数据并且可以操作数据的东西
        const store = redux.createstore(reducer);

        //创建一个视图函数,并且将store显示到视图上。
        const render = ()=>{
            //store的getstate()方法可以得到仓库中的数据
            document.getelementbyid("info").innerhtml = store.getstate().v;
        }
        render(); //调用render函数
        //要将store注册到视图,这样的话,当store中的数据变化候,就能自动调用render函数
        store.subscribe(render);

        // 应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。
        // 惟一改变state的办法是触发action,一个描述发生什么的对象。
        // 为了描述 action 如何改变 state 树,你需要编写 reducers。
        document.getelementbyid("btn1").onclick = function(){
            // 改变内部 state 惟一方法是 dispatch 一个 action,type表示要做的动作
            store.dispatch({"type":"add"})
        }
        document.getelementbyid("btn2").onclick = function(){
            store.dispatch({"type":"minus"})
        }
        document.getelementbyid("btn3").onclick = function(){
            store.dispatch({"type":"cheng"})
        }
    </script>
</body>

创建一个叫做reducer的函数,reducer中“维持”一个量,叫state,初始值是{"v" : 0}

 

这个函数你必须知道两点:

1)它是一个纯函数,在函数内部不改变{"v" : 0}的,不改变state对象的,只是返回了新的state

const reducer = (state = {"v" : 0} , action)=>{
    if(action.type == "add"){
        return {"v" : state.v + 1};
    }else if(action.type == "minus"){
        return {"v" : state.v - 1};
    }
    return state;
}

 

2)这个函数提供了可被预测的功能。

reducer中的state,就像被关进了保险箱。任何对这个state的变化,只能是通过dispatch一个action来改变的。换句话说,只有dispatch一个action才能改变这个保险箱中的数据

reducer函数中,用if语句来表示对state可能发生变化的罗列,只有罗列在楼层里面的改变,才会发生:

if(action.type==""){
    return 新的state
}else if(action.type ==""){
    return 新的state
}else if(action.type ==""){
    return 新的state
}

创建store仓库,这个仓库创建时需要提供reducer,所以可以认为store就是reducerreducer就是store

const store = redux.createstore(reducer);

reducer是一个纯函数,而store提供了三个方法:

 store.subscribe() 注册到视图

 store.getstate() 得到数据

 store.dispatch() 发送action

 

学习reduxreact结合,到时候就不用注册到视图。

然后创建监听:

document.getelementbyid("btn1").onclick = function(){
     store.dispatch({"type":"add"})
}
document.getelementbyid("btn2").onclick = function(){
     store.dispatch({"type":"minus"})
}
document.getelementbyid("btn3").onclick = function(){
     store.dispatch({"type":"cheng"})
}

点击按钮,storedispatch出一个action。所谓的action就是一个json,这个json必须有type属性,值为大写字母。这个action没有任何意义,比如{"type":"add"},但reducer认识这个action,可以产生变化!

 

案例2,添加载荷:

<body>
    <h1 id="info"></h1>
    <button id="btn1">加</button>

    <input type="text" id="txt">
<button id="btn2">加输入的</button>

    <script type="text/javascript" src="redux.min.js"></script>
    <script type="text/javascript">
        const reducer = (state = {"v" : 10}, {type, data})=>{
            if(type == "add"){
            return {"v" : state.v + action.data}
          return {"v" : state.v + data}
            }
            return state;
        }
        const store = redux.createstore(reducer);
        const render = ()=>{
            document.getelementbyid("info").innerhtml = store.getstate().v;
        }
        render();
        store.subscribe(render);

    document.getelementbyid("btn1").onclick = function(){
        store.dispatch({ type: 'add', data:100});
    }

        document.getelementbyid("btn2").onclick = function(){
            var a = parseint(document.getelementbyid("txt").value);
            //载荷payload。
            store.dispatch({"type":"add" , data:a});
        }
    </script>
</body>

唯一可以改变state的方式dispatch一个action

 

案例3:添加数组

<body>
    <div id="box">
        <p><input type="text" id="name"></p>
        <p><input type="text" id="age"></p>
        <button id="btn">增加</button>
        <ul id="list">

        </ul>
    </div>

    <script type="text/javascript" src="redux.min.js"></script>
    <script type="text/javascript">
        //初始数据
        const initobj = {
            "arr" : [
                {"name" : "小明" , "age" : 12},
                {"name" : "小红" , "age" : 13},
                {"name" : "小强" , "age" : 14}
            ]
        };
        //reducer函数
        const reducer = (state = initobj, {type , name , age}) => {
            if(type == "add_student"){
                //不能push改变传入的原数组,所以要返回新数组
                return {
                    "arr" : [
                        {name , age} ,
                        ...state.arr
                    ]
                }
            }
            return state;
        }
        //创建store
        const store = redux.createstore(reducer);
        //视图函数
        const render = function(){
            //清空ul
            document.getelementbyid("list").innerhtml = "";
            //创建li
            for(var i = 0 ; i < store.getstate().arr.length ; i++){
                var li = document.createelement("li");
            var storearr = store.getstate().arr[i]
                li.innerhtml = storearr.name + storearr.age+"岁"
                document.getelementbyid("list").appendchild(li);
            }
        }
        render();//运行视图函数
        store.subscribe(render);//注册到视图
        
        document.getelementbyid("btn").onclick = function(){
            var name = document.getelementbyid("name").value;
            var age = document.getelementbyid("age").value;
            //发出action,这是唯一能够改变store的途径
            store.dispatch({"type" : "add_student", name, age })
        }
    </script>
</body>

 

案例4,加深练习:

<body>
    <h1 id="info"></h1>
    <h1 id="info2"></h1>
    <button id="btn">+</button>

    <script type="text/javascript" src="redux.min.js"></script>
    <script type="text/javascript">
        var initobj = {
            "a" : {
                "b" : {
                    "c" : {
                        "v" : 10
                    }
                },
                "m" : 8
            }
        }
        const reducer = (state = initobj , action) => {
            if(action.type == "add"){
                return {
                    ...state, 
                    "a" : {
                        ...state.a ,
                        "b" : {
                            ...state.a.b,
                            "c" : {
                                ...state.a.b.c ,
                                "v" : state.a.b.c.v + 1
                            }
                        }
                    }
                }
            }
            return state;
        }
        const store = redux.createstore(reducer);
        const render = ()=>{
            document.getelementbyid("info").innerhtml = store.getstate().a.b.c.v;
            document.getelementbyid("info2").innerhtml = store.getstate().a.m;
        }
        render();
        store.subscribe(render);

        document.getelementbyid("btn").onclick = function(){
            store.dispatch({"type" : "add"});
        }
    </script>
</body>

二、reduxreact进行结合开发

react开发的时候使用redux可预测状态容器,要装两个新的依赖:

 redux:提供createstorecombinereducersbindactioncreators等功能

 react-redux:只提供两个东西,<provider>组件、connect()函数。

 

安装两个依赖:

npm install --save redux react-redux

 

创建reducers文件夹,创建index.js,这个文件暴露一个纯函数:

reducers/index.js

export default (state = { v : 10}, action) => {
    return state;
}

 

main.js入口文件:

import react from "react";
import reactdom from "react-dom";
import {createstore} from "redux";
import app from "./app.js";
import reducer from "./reducers";  //reducer函数

//创建 redux store 来存放应用的状态。
const store = createstore(reducer);

reactdom.render(
    <app></app>,
    document.getelementbyid("app")
);

有两个问题:

如何让组件能够访问到store中的数据?

如果让store中的数据改变的时候,能够自动更新组件的视图

 

react-redux解决了这两个问题。

main.js中创建<provider>组件,它提供的是一个顶层容器的作用,实现store的上下文传递,是让store能够“注入”到所有组件

react-redux提供provider组件,可以让容器组件拿到state

provider在根组件外面包一层,这样一来,app所有的子组件就默认都拿到了state了。

原理是react组件的context属性,就是将store这个对象放到上下文(context)中

import react from "react";
import reactdom from "react-dom";
import {createstore} from "redux";
import {provider} from "react-redux";
import app from "./app.js";
import reducer from "./reducers";

//创建store仓库
const store = createstore(reducer);

reactdom.render(
    <provider store={store}>
        <app></app>
    </provider>,
    document.getelementbyid('app')
);

组件中要获得全局store的值,需要用connect函数,connect提供连接react组件与redux store的作用。

connect([mapstatetoprops], [mapdispatchtoprops])

connect()的第一个参数:mapstatetoprops这个函数的第一个参数就是reduxstore,会自动将store的数据作为props绑定到组件上。

connect()的第二个参数:mapdispatchtoprops它的功能是,将action作为props绑定到组件上

 

通俗理解,使用connect可以把statedispatch绑定到react组件,使得组件可以访问到redux的数据。

常看到下面这种写法:

export default connect()(app)

app.js

import react from "react";
import {connect} from "react-redux";
class app extends react.component {
    constructor() {
        super();
     
    }
    render(){
        return <div>
            <h1>{this.props.v}</h1>
        </div>
    }
}
export default connect(
//这个函数return的对象的值,将自动成为组件的props。
    (state) => {
        return {
            v : state.v
        }
    }
)(app);

一旦connect()(app); 连接某个组件,此时这个组件就会:当全局store发送改变时,如同自己的props发生改变一样,从而进行视图更新。

 

app.js写两个按钮,可以加1,减1

import react from "react";
import {connect} from "react-redux";
class app extends react.component {
    constructor() {
        super();
    }
    render(){
        return <div>
            <h1>{this.props.v}</h1>
            <button onclick={()=>{this.props.add()}}>+</button>
            <button onclick={()=>{this.props.minus()}}>-</button>
        </div>
    }
}
export default connect(
    (state) => {
        return {
            v : state.v
        }
    },
    (dispatch) => {
        return {
            add(){
                dispatch({"type" : "add"});
            },
            minus(){
                dispatch({"type" : "minus"});
            }
        }
    }
)(app);

reducers/index.js提供可预测状态

export default (state = {"v" : 10} , action) => {
    if(action.type == "add"){
        return { "v" : state.v + 1 };
    }else if(action.type == "minus"){
        return { "v" : state.v - 1 };
    }
    return state;
}

 

学生管理系统小案例:

reducers/index.js

const initobj = {
    "arr" : [
        {"id" : 1, "name" : "小明" , "age" : 12},
        {"id" : 2, "name" : "小红" , "age" : 13},
        {"id" : 3, "name" : "小刚" , "age" : 14}  
    ]
}
export default (state = initobj, {type, age, name, id})=>{
    if(type == "add_student"){
        return {
            ...state , 
            "arr" : [
                ...state.arr ,
                {
                    "id" : state.arr.reduce((a,b)=>{
                        return b.id > a ? b.id : a;
                    },0) + 1,
                    name, 
                    age
                }
            ]
        }
    }else if(type == "del_student"){
        return {
            ...state,
            "arr" : state.arr.filter(item=>item.id != id)
        }
    }
    return state;
}

 

app.js

import react from "react";
import {connect} from "react-redux";
class app extends react.component {
    constructor() {
        super();
    }
    //增加学生
    add(){
        var name = this.refs.name.value;
        var age = this.refs.age.value;
        this.props.addstudent(name, age)
    }
    render(){
        return <div>
            <p><input type="text" ref="name"/></p>
            <p><input type="text" ref="age"/></p>
            <button onclick={()=>{this.add()}}>增加学生</button>
            {
                this.props.arr.map((item,index)=>{
                    return <p key={item.id}>
                        {item.id} {item.name} {item.age}岁
                        <button onclick={()=>{this.props.delstudent(item.id)}}>删除</button>
                    </p>
                })
            }
        </div>
    }
}
export default connect(
//(state) => {
    //    return { arr : state.arr }
    //},
//简化写法
    ({arr})=>{
        return { arr }
    },
    (dispatch)=>{
        return {
            addstudent(name, age){
                dispatch({"type" : "add_student", name, age})
            },
            delstudent(id){
                dispatch({"type" : "del_student" , id})
            }
        }
    }
)(app);

原理解析

首先connect之所以会成功,是因为provider组件:

在原应用组件上包裹一层,使原来整个应用成为provider的子组件

接收reduxstore作为props,通过context对象传递给子孙组件上的connect

 

connect做了些什么?

它真正连接 redux react,它包在我们的容器组件的外一层,它接收provider提供的 store 里面的state dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。

 

总结:

connect()(app),第一个()中接受两个参数,分别是:mapstatetopropsmapdispatchtoprops

这两个参数都是函数,第一个参数函数return的对象的键名将自动和props进行绑定,第二个参数函数return的对象的键名,也将和props进行绑定。

第一个参数return的对象,是从state中获得值

第二个参数return的对象,是要改变state的值

 

如果有兴趣,可以看一下connect函数的api文档:

不管应用程序有多大,store只有一个,它就像天神一样“照耀”所有组件,但默认情况下所有组件是不能得到store的数据的,哪个组件要拿数据,就要connect一下,另外app最大组件确实包裹着所有组件,但不代表app组件连接了就代表其他组件也连接了。

 


三、redux编程-todolist

reducers/index.js中根据项目情况创建reducer

const initobj = {
    "todos": [
       {"id" : 1, "title" : "吃饭",  "done" : false},
       {"id" : 2, "title" : "睡觉",  "done" : false},
       {"id" : 3, "title" : "打豆豆","done" : false}
    ]
}

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

 

分别创建todohd.jstodobd.jstodoft.js三个组件:

import react from "react";
export default class todohd extends react.component {
    constructor() {
        super();
    }

    render() {
        return <div>
            <h1>我是todohd组件</h1>
        </div>
    }
}

 

 

 

 

app.js引入组件

import react from "react";
import {connect} from "react-redux";

import todohd from "./components/todohd.js";
import todobd from "./components/todobd.js";
import todoft from "./components/todoft.js";

class app extends react.component {
    constructor() {
        super();
    }
    
    render() {
        return <div>
            <todohd></todohd>
            <todobd></todobd>
            <todoft></todoft>
        </div>
    }
}
export default connect()(app)  
import react from 'react';
import {connect} from "react-redux";
import todoitem from "./todoitem.js";
class todobd extends react.component {
    constructor(props){
        super(props);
    }
    render() {
        return (
            <div>
            //{json.stringify(this.props.todos)}
                {
                    this.props.todos.map(item=>{
                    //return <div key={item.id}>{item.title}</div>
                        return <todoitem key={item.id} item={item}></todoitem>
                    })
                }
            </div>
        );
    }
}  
//connect目的是问“天”要数据,要通天。
//“通天”是比喻,就是说要问store要数据
export default connect(
    (state)=>{
        return {
            todos : state.todos
        }
    }
)(todobd);

 

todoitem.js

import react from 'react';
export default class todoitem extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div classname="todoitem">
                <input type="checkbox" checked={this.props.item.done}/>
                <span>{this.props.item.title}</span>
                <button>删除</button>
            </div>
        );
    }
}

 

todohd.js增加待办事项

import react from 'react';
import {connect} from "react-redux";
class todohd extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                <input type="text" ref="titletxt"/>
                <button onclick={()=>{
                    this.props.addtodo(this.refs.titletxt.value);
                    this.refs.titletxt.value = "";
                }}>添加</button>
            </div>
        );
    }
}
//这个组件要通天的目的不是要数据,而是改变数据
export default connect(
    null ,
    (dispatch)=>{
        return {
            addtodo(title){
                dispatch({"type":"addtodo", title})
            }
        }
    }
)(todohd);

 

reducers/index.js写可预测状态  

const initobj = {
    "todos" : [
        ...
    ],
}
export default (state = initobj, action)=>{
    if(action.type == "addtodo"){
        return {
            ...state, 
            "todos" : [
                ...state.todos, 
                {
                    "id" : state.todos.reduce((a,b)=>{
                        return b.id > a ? b.id : a;
                    },0) + 1,
                    "title" : action.title, 
                    "done" : false
                }
            ]
        }
    }
    return state;
}

todoft.js

import react from "react";
import {connect} from "react-redux";
class todoft extends react.component {
    constructor() {
        super();
    }

    render() {
        return <div>
            当前:共{this.props.todos.length}条信息--
            已做{this.props.todos.filter(item => item.done).length}条--
            未做{this.props.todos.filter(item => !item.done).length}条
        </div>
    }
}

export default connect(
    (state)=>{
        return {
            todos:state.todos
        }
    }
)(todoft)

因为todoitem这个组件是不通天的,所以todoitem是不能自己独立dispatchstore的。

此时就需要todobd帮助,因为todobd是通天。

 

这是套路:所有被循环语句map出来的组件,一律不通天,数据父亲给,改变store的能力父亲给。

todobd.js组件引入了todoitem.js组件,因为todoitem组件是被map出来的,所以信息要传给每一个todoitem,而不是让todoitem自己通天拿数据。

todobd.js

import react from 'react';
import {connect} from "react-redux";
import todoitem from "./todoitem.js";

class todobd extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        //根据全局的show属性来决定当前todos数组
        if(this.props.show == "all"){
            var todos = this.props.todos;
        }else if(this.props.show == "onlydone"){
            var todos = this.props.todos.filter(item=>item.done);
        }else if(this.props.show == "onlyundone"){
            var todos = this.props.todos.filter(item=>!item.done);
        }

        return (
            <div>
                {
                    todos.map(item=>{
                        return <todoitem
                            key={item.id}
                            item={item}
                            deltodo={this.props.deltodo.bind(this)}
                            changetodo={this.props.changetodo.bind(this)}
                        ></todoitem>
                    })
                }
            </div>
        );
    }
}
export default connect(
    (state)=>{
        return {
            todos : state.todos ,
            show : state.show
        }
    },
    (dispatch)=>{
        return {
            deltodo(id){
                dispatch({"type" : "deltodo", id});
            },
            changetodo(id , k , v){
                dispatch({"type" : "changetodo", id, k, v});
            }
        }
    }
)(todobd);

 

todoitem.js

import react from 'react';
export default class todoitem extends react.component {
    constructor(props) {
        super(props);
        this.state = {
            "onedit" : false
        }
    }
    render() {
     const {id, title, done} = this.props.item;
        return (
            <div>
                <input 
                    type="checkbox" checked={done}
                    onchange={(e)=>{
              this.props.changetodo(id, "done", e.target.checked)
            }}
                />
                {
                    this.state.onedit 
                    ?
                    <input 
                        type="text" 
                        defaultvalue={title} 
                        onblur={(e)=>{
                            this.props.changetodo(id,"title", e.target.value)
                            this.setstate({"onedit" : false})
                        }}
                    />
                    :
                    <span ondoubleclick={()=>{this.setstate({"onedit":true})}}>
            {title}
          </span>
                }

                <button onclick={()=>{this.props.deltodo(id)}}>删除</button>
            </div>
        );
    }
}

 

index.js

const initobj = {
    "todos" : [
        ...
    ],
    "show" : "all"       //all、onlydone、onlyundone
}
export default (state = initobj, action) => {
    if(action.type == "addtodo"){
        ...
    }else if(action.type == "deltodo"){
        return {
            ...state, 
            "todos" : state.todos.filter(item => item.id != action.id)
        }
    }else if(action.type == "changetodo"){
        return {
            ...state, 
            "todos" : state.todos.map(item => {
                //如果遍历到的item项和传入的aciton的id项不一样,此时返回原item
                if(item.id != action.id) return item;
                //否则返回修改之后的item
                return {
                    ...item ,
                    [action.k] : action.v
                }
            })
        }
    }else if(action.type == "changeshow"){
        return {
            ...state, 
            "show" : action.show
        }
    }
    return state;
}

 

todoft.js

import react from 'react';
import {connect} from "react-redux";
import classnames from "classnames";

class todoft extends react.component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div>
                <p>
                    当前共{this.props.todos.length}条信息
做完{this.props.todos.filter(item=>item.done).length}条
未做{this.props.todos.filter(item=>!item.done).length}条
                </p>
                <p>
                    <button classname={classnames({"cur":this.props.show == 'all'})}                                 
            onclick={()=>{this.props.changeshow('all')}}>查看全部             </button> <button classname={classnames({"cur":this.props.show == 'onlydone'})} onclick={()=>{this.props.changeshow('onlydone')}}>仅看已做 </button> <button classname={classnames({"cur":this.props.show == 'onlyundone'})} onclick={()=>{this.props.changeshow('onlyundone')}}>仅看未做 </button> </p> </div> ); } } export default connect( (state) => { return { todos : state.todos , show : state.show } }, (dispatch) => { return { changeshow(show){ dispatch({"type" : "changeshow" , show}) } } } )(todoft);

四、logger插件

redux-logger用来辅助开发。

npm install --save redux-logger

改变main.js:

import react from "react";
import reactdom from "react-dom";
import {createstore , applymiddleware} from "redux";
import {provider} from "react-redux";
import logger from "redux-logger";

import app from "./app.js";
//引入reducer
import reducer from "./reducers/index.js";

//创建store
const store = createstore(reducer , applymiddleware(logger));

reactdom.render(
    <provider store={store}>
        <app></app>
    </provider>
    ,
    document.getelementbyid("app-container")
);

也可以使用redux-devtools这个插件。

npm install --save-dev redux-devtools
npm install --save-dev redux-devtools-log-monitor
npm install --save-dev redux-devtools-dock-monitor
npm install --save-dev redux-devtools-chart-monitor

文档:

https://github.com/reduxjs/redux-devtools/blob/master/docs/walkthrough.md

 


五、combinereducersbindactioncreators

一个网页的应用程序可能是多个reducer,合并为一个reducer,比如countertodoreducer

 

redux提供的combinereducers方法,用于reducer的拆分,只要定义各个子reducer函数,然后用这个方法,将它们合成一个大的reducer

redux提供的bindactioncreators方法,用于通过dispatchaction包裹起来,这条可以通过bindactioncreators创建的方法,直接调用dispatch(action)“隐式调用”。


5.1 combinereducers

reducers/counter.js就是一个普通的纯函数:

export default (state = {"v" : 10},action)=>{
    return state;
}

reducers/todo.js提供的数据:

const initobj = {
    "todos": [
        { "id": 1, "title": "吃饭", "done": false },
        { "id": 2, "title": "睡觉", "done": false },
        { "id": 3, "title": "打豆豆", "done": true }
    ] 
};

export default (state = initobj, action) => {
    return state
}

 

 

 

 

reducers/index.js要用redux提供的combinereducers来进行智能合并

import { combinereducers } from "redux";
import counter from "./counter.js";
import todos from "./todos.js";

//暴露合并的reducer
export default combinereducers({
    counter,
    todos
})

 

main.js

import react from "react";
import reactdom from "react-dom";
import {createstore} from "redux";
import {provider} from "react-redux";

import app from "./containers/app.js";
//引入reducer
import reducer from "./reducers";

// 创建 redux store 来存放应用的状态。
const store = createstore(reducer);

reactdom.render(
    <provider store={store}>
        <app></app>
    </provider>,
    document.getelementbyid("app")
);

 

containers/app.js组件使用数据

import react from 'react';
import {connect} from "react-redux";
class app extends react.component {
    constructor(){
        super();
    }
    render() {
        return (
            <div>
                <h1>{this.props.v}</h1>
            </div>
        );
    }
}
export default connect(
    ({counter}) => ({
        v : counter.v
    })
)(app);

 

components/todolist/index.js组件

import react from "react";
import { connect } from "react-redux";
class todolist extends react.component {
    constructor(){
        super();
    }
    render() {
        return (
            <div>
                <h1>我是todolist</h1>
                {
                    this.props.todos.map(item=>{
                        return <p key={item.id}>{item.title}</p>
                    })
                }
            </div>
        )
    }
};
export default connect(
   ({todos: {todos}})=>{
        return {
            todos
        }
   }
)(todolist)

 

containers/app.js引入组件:

import react from 'react';
import todolist from "../components/todolist/index.js";
import counter from "../components/counter/index.js";
 
export default class app extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                <counter></counter>
                <todolist></todolist>
            </div>
        );
    }
}

5.2 bindactioncreators

bindactioncreators会将actiondispatch绑定并返回一个对象,这个对象会作为props的一部分传入组件中。

bindactioncreators主要作用:一般情况下,可以通过providerstore通过reactconnext属性向下传递,bindactioncreators的唯一用处就是需要传递action creater到子组件,并且该子组件并没有接收到父组件上传递的storedispatch

官方的文件夹结构:

 

actions/counteractions.js新建actions文件夹存放type

// 我们把return一个action的函数叫做“action creator”
// 所以这个文件向外暴露了几个动作
export const add = () => ({ type: "add" })
export const minus = () => ({ type: "minus" })
export const cheng = () => ({ type: "cheng" })
export const chu = () => ({ type: "chu" })

 

counter/index.js计数器组件

import react from 'react';
import {bindactioncreators} from "redux";
import {connect} from "react-redux";
import * as counteractions from "../../actions/counteractions.js";

class counter extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                <h1>counter : {this.props.v}</h1>
                <button onclick={()=>{this.props.counteractions.add()}}>加</button>
                <button onclick={()=>{this.props.counteractions.minus()}}>减</button>
                <button onclick={()=>{this.props.counteractions.cheng()}}>乘</button>
                <button onclick={()=>{this.props.counteractions.chu()}}>除</button>
            </div>
        );
    }
}
export default connect(
    ({counter}) => ({
        v : counter.v
    }),
    (dispatch) => ({
    //这里的dispatch,等同于store中的store.dispatch,用于组合action
        counteractions : bindactioncreators(counteractions , dispatch)
    })
)(counter);

 

app/reducers/counter.js

import {add, minus, cheng, chu} from "../constants/counter.js";

export default (state = {"v" : 0} , action) => {
    if(action.type == "add"){
        return {
            ...state , 
            "v" : state.v + 1
        }
    }else if(action.type == "minus"){
        return {
            ...state , 
            "v" : state.v - 1
        }
    }else if(action.type == "cheng"){
        return {
            ...state , 
            "v" : state.v * 2
        }
    }else if(action.type == "chu"){
        return {
            ...state , 
            "v" : state.v / 2
        }
    }
    return state;
}

 

 

 

 

todolist/index.js

import react from 'react';
import todohd from "./todohd.js";
import todobd from "./todobd.js";

export default class todolist extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
              <div>
                  <h1>todolist</h1>
                  <todohd></todohd>
                  <todobd></todobd>
              </div>
        );
    }
}

 

todohd.js

import react from 'react';
import {bindactioncreators} from "redux";
import {connect} from "react-redux";
import * as todoactions from "../../actions/todoactions.js";

class todohd extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                <input type="text" ref="titletxt"/>
                <button onclick={()=>{this.props.todoactions.add(this.refs.titletxt.value)}}
            >添加
          </button>
            </div>
        );
    }
}
export default connect(
    null ,
    (dispatch) => ({
        todoactions : bindactioncreators(todoactions , dispatch)
    })
)(todohd);

 

todobd.js

import react from 'react';
import {bindactioncreators} from "redux";
import {connect} from "react-redux";
import * as todoactions from "../../actions/todoactions.js";

class todobd extends react.component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                {
                    this.props.todos.map(item=>{
                        return <p key={item.id}>
                            {item.title}
                            <button onclick={()=>{this.props.todoactions.del(item.id)}}>
                 删除
                  </button>
                        </p>
                    })
                }
            </div>
        );
    }
}

export default connect(
    ({todo}) => ({
        todos : todo.todos
    }) ,
    (dispatch) => ({
        todoactions : bindactioncreators(todoactions , dispatch)
    })
)(todobd);

 

为了防止actiontype命名冲突,此时要单独存放在const文件夹中:

app\constants\counter.js

export const add = "add_counter";
export const minus = "minus_counter";
export const cheng = "cheng_counter";
export const chu = "chu_counter";

app\constants\todo.js

export const add = "add_todo";
export const del = "del_todo";

然后就可以在以下文件中,引入以上常量,然后使用大写的常量替换type字符串

l actions中的counteractions.jstodoactions.js

l reducers中的todo.jscounter.js

 

actions/todoactions.js

import {add , del} from "../constants/todo.js";
export const add = (title) => ({"type" : add, title});
export const del = (id) => ({"type" : del, id});

actions/counteractions.js

import {add , minus , cheng , chu} from "../constants/counter.js";
export const add = () => ({"type" : add});
export const minus = () => ({"type" : minus});
export const cheng = () => ({"type" : cheng});
export const chu = (n) => ({"type" : chu , n});

 

reducers/todo.js

import {add , del} from "../constants/todo.js";
const initobj = {
    "todos" : [
        {"id" : 1 , "title" : "吃饭" , "done" : false},
        {"id" : 2 , "title" : "睡觉" , "done" : false},
        {"id" : 3 , "title" : "打豆豆" , "done" : false}
    ]
}
export default (state = initobj , action) => {
    if(action.type == add){
        return {
            ...state ,
            "todos" : [
                ...state.todos ,
                {
                    "id" : state.todos.reduce((a,b)=>{return b.id > a ? b.id : a},0) + 1,
                    "title": action.title,
                    "done" : action.done
                }
            ]
        }
    }else if(action.type == del){
        return {
            ...state ,
            "todos" : state.todos.filter(item => item.id != action.id)
        }
    }
    return state;
}

 

reducers/counter.js

import {add , minus , cheng , chu} from "../constants/counter.js";

export default (state = {"v" : 0} , action) => {
    if(action.type == add){
        return {
            ...state , 
            "v" : state.v + 1
        }
    }else if(action.type == minus){
        return {
            ...state , 
            "v" : state.v - 1
        }
    }else if(action.type == cheng){
        return {
            ...state , 
            "v" : state.v * 2
        }
    }else if(action.type == chu){
        return {
            ...state , 
            "v" : state.v / action.n
        }
    }
    return state;
}

 

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网