欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
  首先要引redux.js包,这个包提供了Redux对象,这个对象可以调用Redux.createStore()方法。
 
  <body>
 
  <h1id="info"></h1>
 
  <buttonid="btn1">加</button>
 
  <buttonid="btn2">减</button>
 
  <buttonid="btn3">乘</button>
 
  <buttonid="btn4">除</button>
 
  <scripttype="text/javascript"src="redux.min.js"></script>
 
  <scripttype="text/javascript">
 
  //这是一个reducer,形式为(state,action)=>state的纯函数。
 
  //纯函数:函数内部,不改变传入的参数,只return新值。
 
  //描述了action如何把state转变成下一个state。
 
  constreducer=(state={"v":10},action)=>{
 
  if(action.type=="ADD"){
 
  return{"v":state.v+1};
 
  }elseif(action.type=="MINUS"){
 
  return{"v":state.v-1};
 
  }elseif(action.type=="CHENG2"){
 
  return{"v":state.v*2}
 
  }
 
  returnstate;
 
  }
 
  //创建Reduxstore来存放应用的状态。
 
  //store翻译为“仓库”,这是一个存放数据并且可以操作数据的东西
 
  conststore=Redux.createStore(reducer);
 
  //创建一个视图函数,并且将store显示到视图上。
 
  constrender=()=>{
 
  //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。
 
  constreducer=(state={"v":0},action)=>{
 
  if(action.type=="ADD"){
 
  return{"v":state.v+1};
 
  }elseif(action.type=="MINUS"){
 
  return{"v":state.v-1};
 
  }
 
  returnstate;
 
  }
 
  2)这个函数提供了可被预测的功能。
 
  reducer中的state,就像被关进了保险箱。任何对这个state的变化,只能是通过dispatch一个action来改变的。换句话说,只有dispatch一个action才能改变这个保险箱中的数据。
 
  在reducer函数中,用if语句来表示对state可能发生变化的罗列,只有罗列在楼层里面的改变,才会发生:
 
  if(action.type==""){
 
  return新的state
 
  }elseif(action.type==""){
 
  return新的state
 
  }elseif(action.type==""){
 
  return新的state
 
  }
 
  示例代码
 
  创建store仓库,这个仓库创建时需要提供reducer,所以可以认为store就是reducer,reducer就是store。
 
  conststore=Redux.createStore(reducer);
 
  reducer是一个纯函数,而store提供了三个方法:
 
  store.subscribe()注册到视图
 
  store.getState()得到数据
 
  store.dispatch()发送action
 
  学习redux和react结合,到时候就不用注册到视图。
 
  然后创建监听:
 
  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"})
 
  }
 
  示例代码
 
  点击按钮,store要dispatch出一个action。所谓的action就是一个JSON,这个JSON必须有type属性,值为大写字母。这个action没有任何意义,比如{"type":"ADD"},但reducer认识这个action,可以产生变化!
 
  案例2,添加载荷:
 
  <body>
 
  <h1id="info"></h1>
 
  <buttonid="btn1">加</button>
 
  <inputtype="text"id="txt">
 
  <buttonid="btn2">加输入的</button>
 
  <scripttype="text/javascript"src="redux.min.js"></script>
 
  <scripttype="text/javascript">
 
  constreducer=(state={"v":10},{type,data})=>{
 
  if(type=="ADD"){
 
  return{"v":state.v+action.data}
 
  return{"v":state.v+data}
 
  }
 
  returnstate;
 
  }
 
  conststore=Redux.createStore(reducer);
 
  constrender=()=>{
 
  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(){
 
  vara=parseInt(document.getElementById("txt").value);
 
  //载荷payload。
 
  store.dispatch({"type":"ADD",data:a});
 
  }
 
  </script>
 
  </body>
 
  唯一可以改变state的方式dispatch一个action
 
  案例3:添加数组
 
  <body>
 
  <divid="box">
 
  <p><inputtype="text"id="name"></p>
 
  <p><inputtype="text"id="age"></p>
 
  <buttonid="btn">增加</button>
 
  <ulid="List">
 
  </ul>
 
  </div>
 
  <scripttype="text/javascript"src="redux.min.js"></script>
 
  <scripttype="text/javascript">
 
  //初始数据
 
  constinitObj={
 
  "arr":[
 
  {"name":"小明","age":12},
 
  {"name":"小红","age":13},
 
  {"name":"小强","age":14}
 
  ]
 
  };
 
  //reducer函数
 
  constreducer=(state=initObj,{type,name,age})=>{
 
  if(type=="ADD_STUDENT"){
 
  //不能push改变传入的原数组,所以要返回新数组
 
  return{
 
  "arr":[
 
  {name,age},
 
  ...state.arr
 
  ]
 
  }
 
  }
 
  returnstate;
 
  }
 
  //创建store
 
  conststore=Redux.createStore(reducer);
 
  //视图函数
 
  constrender=function(){
 
  //清空ul
 
  document.getElementById("List").innerHTML="";
 
  //创建li
 
  for(vari=0;i<store.getState().arr.length;i++){
 
  varli=document.createElement("li");
 
  varstoreArr=store.getState().arr[i]
 
  li.innerHTML=storeArr.name+storeArr.age+"岁"
 
  document.getElementById("List").appendChild(li);
 
  }
 
  }
 
  render();//运行视图函数
 
  store.subscribe(render);//注册到视图
 
  document.getElementById("btn").onclick=function(){
 
  varname=document.getElementById("name").value;
 
  varage=document.getElementById("age").value;
 
  //发出Action,这是唯一能够改变store的途径
 
  store.dispatch({"type":"ADD_STUDENT",name,age})
 
  }
 
  </script>
 
  </body>
 
  案例4,加深练习:
 
  <body>
 
  <h1id="info"></h1>
 
  <h1id="info2"></h1>
 
  <buttonid="btn">+</button>
 
  <scripttype="text/javascript"src="redux.min.js"></script>
 
  <scripttype="text/javascript">
 
  varinitObj={
 
  "a":{
 
  "b":{
 
  "c":{
 
  "v":10
 
  }
 
  },
 
  "m":8
 
  }
 
  }
 
  constreducer=(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
 
  }
 
  }
 
  }
 
  }
 
  }
 
  returnstate;
 
  }
 
  conststore=Redux.createStore(reducer);
 
  constrender=()=>{
 
  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>
 
  二、Redux和React进行结合开发
 
  在React开发的时候使用Redux可预测状态容器,要装两个新的依赖:
 
  redux:提供createStore、combineReducers、bindActionCreators等功能
 
  react-redux:只提供两个东西,<Provider>组件、connect()函数。
 
  安装两个依赖:
 
  npminstall--savereduxreact-redux
 
  创建reducers文件夹,创建index.js,这个文件暴露一个纯函数:
 
  reducers/index.js:
 
  exportdefault(state={v:10},action)=>{
 
  returnstate;
 
  }
 
  main.js入口文件:
 
  importReactfrom"react";
 
  importReactDOMfrom"react-dom";
 
  import{createStore}from"redux";
 
  importAppfrom"./App.js";
 
  importreducerfrom"./reducers";//reducer函数
 
  //创建Reduxstore来存放应用的状态。
 
  conststore=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)中
 
  importReactfrom"react";
 
  importReactDOMfrom"react-dom";
 
  import{createStore}from"redux";
 
  import{Provider}from"react-redux";
 
  importAppfrom"./App.js";
 
  importreducerfrom"./reducers";
 
  //创建store仓库
 
  conststore=createStore(reducer);
 
  ReactDOM.render(
 
  <Providerstore={store}>
 
  <App></App>
 
  </Provider>,
 
  document.getElementById('app')
 
  );
 
  组件中要获得全局store的值,需要用connect函数,connect提供连接React组件与Reduxstore的作用。
 
  connect([mapStateToProps],[mapDispatchToProps])
 
  connect()的第一个参数:mapStateToProps这个函数的第一个参数就是Redux的store,会自动将store的数据作为props绑定到组件上。
 
  connect()的第二个参数:mapDispatchToProps它的功能是,将action作为props绑定到组件上
 
  通俗理解,使用connect可以把state和dispatch绑定到React组件,使得组件可以访问到redux的数据。
 
  常看到下面这种写法:
 
  exportdefaultconnect()(App)
 
  App.js
 
  importReactfrom"react";
 
  import{connect}from"react-redux";
 
  classAppextendsReact.Component{
 
  constructor(){
 
  super();
 
  }
 
  render(){
 
  return<div>
 
  <h1>{this.props.v}</h1>
 
  </div>
 
  }
 
  }
 
  exportdefaultconnect(
 
  //这个函数return的对象的值,将自动成为组件的props。
 
  (state)=>{
 
  return{
 
  v:state.v
 
  }
 
  }
 
  )(App);
 
  一旦connect()(App);连接某个组件,此时这个组件就会:当全局store发送改变时,如同自己的props发生改变一样,从而进行视图更新。
 
  在App.js写两个按钮,可以加1,减1:
 
  importReactfrom"react";
 
  import{connect}from"react-redux";
 
  classAppextendsReact.Component{
 
  constructor(){
 
  super();
 
  }
 
  render(){
 
  return<div>
 
  <h1>{this.props.v}</h1>
 
  <buttononClick={()=>{this.props.add()}}>+</button>
 
  <buttononClick={()=>{this.props.minus()}}>-</button>
 
  </div>
 
  }
 
  }
 
  exportdefaultconnect(
 
  (state)=>{
 
  return{
 
  v:state.v
 
  }
 
  },
 
  (dispatch)=>{
 
  return{
 
  add(){
 
  dispatch({"type":"ADD"});
 
  },
 
  minus(){
 
  dispatch({"type":"MINUS"});
 
  }
 
  }
 
  }
 
  )(App);
 
  reducers/index.js提供可预测状态
 
  exportdefault(state={"v":10},action)=>{
 
  if(action.type=="ADD"){
 
  return{"v":state.v+1};
 
  }elseif(action.type=="MINUS"){
 
  return{"v":state.v-1};
 
  }
 
  returnstate;
 
  }
 
  学生管理系统小案例:
 
  reducers/index.js
 
  constinitObj={
 
  "arr":[
 
  {"id":1,"name":"小明","age":12},
 
  {"id":2,"name":"小红","age":13},
 
  {"id":3,"name":"小刚","age":14}
 
  ]
 
  }
 
  exportdefault(state=initObj,{type,age,name,id})=>{
 
  if(type=="ADD_STUDENT"){
 
  return{
 
  ...state,
 
  "arr":[
 
  ...state.arr,
 
  {
 
  "id":state.arr.reduce((a,b)=>{
 
  returnb.id>a?b.id:a;
 
  },0)+1,
 
  name,
 
  age
 
  }
 
  ]
 
  }
 
  }elseif(type=="DEL_STUDENT"){
 
  return{
 
  ...state,
 
  "arr":state.arr.filter(item=>item.id!=id)
 
  }
 
  }
 
  returnstate;
 
  }
 
  App.js
 
  importReactfrom"react";
 
  import{connect}from"react-redux";
 
  classAppextendsReact.Component{
 
  constructor(){
 
  super();
 
  }
 
  //增加学生
 
  add(){
 
  varname=this.refs.name.value;
 
  varage=this.refs.age.value;
 
  this.props.addStudent(name,age)
 
  }
 
  render(){
 
  return<div>
 
  <p><inputtype="text"ref="name"/></p>
 
  <p><inputtype="text"ref="age"/></p>
 
  <buttononClick={()=>{this.add()}}>增加学生</button>
 
  {
 
  this.props.arr.map((item,index)=>{
 
  return<pkey={item.id}>
 
  {item.id}{item.name}{item.age}岁
 
  <buttononClick={()=>{this.props.delStudent(item.id)}}>删除</button>
 
  </p>
 
  })
 
  }
 
  </div>
 
  }
 
  }
 
  exportdefaultconnect(
 
  //(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的子组件
 
  接收Redux的store作为props,通过context对象传递给子孙组件上的connect
 
  connect做了些什么?
 
  它真正连接Redux和React,它包在我们的容器组件的外一层,它接收Provider提供的store里面的state和dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
 
  总结:
 
  connect()(App),第一个()中接受两个参数,分别是:mapStateToProps、mapDispatchToProps。
 
  这两个参数都是函数,第一个参数函数return的对象的键名将自动和props进行绑定,第二个参数函数return的对象的键名,也将和props进行绑定。
 
  第一个参数return的对象,是从state中获得值
 
  第二个参数return的对象,是要改变state的值
 
  如果有兴趣,可以看一下connect函数的API文档:
 
  https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
 
  不管应用程序有多大,store只有一个,它就像天神一样“照耀”所有组件,但默认情况下所有组件是不能得到store的数据的,哪个组件要拿数据,就要connect一下,另外App最大组件确实包裹着所有组件,但不代表App组件连接了就代表其他组件也连接了。
 
  三、Redux编程-TodoList
 
  http://www.todolist.cn/
 
  在reducers/index.js中根据项目情况创建reducer:
 
  constinitObj={
 
  "todos":[
 
  {"id":1,"title":"吃饭","done":false},
 
  {"id":2,"title":"睡觉","done":false},
 
  {"id":3,"title":"打豆豆","done":false}
 
  ]
 
  }
 
  exportdefault(state=initObj,action)=>{
 
  returnstate;
 
  }
 
  分别创建TodoHd.js、TodoBd.js、TodoFt.js三个组件:
 
  importReactfrom"react";
 
  exportdefaultclassTodoHdextendsReact.Component{
 
  constructor(){
 
  super();
 
  }
 
  render(){
 
  return<div>
 
  <h1>我是TodoHd组件</h1>
 
  </div>
 
  }
 
  }
 
  示例代码
 
  App.js引入组件
 
  importReactfrom"react";
 
  import{connect}from"react-redux";
 
  importTodoHdfrom"./components/TodoHd.js";
 
  importTodoBdfrom"./components/TodoBd.js";
 
  importTodoFtfrom"./components/TodoFt.js";
 
  classAppextendsReact.Component{
 
  constructor(){
 
  super();
 
  }
 
  render(){
 
  return<div>
 
  <TodoHd></TodoHd>
 
  <TodoBd></TodoBd>
 
  <TodoFt></TodoFt>
 
  </div>
 
  }
 
  }
 
  exportdefaultconnect()(App)
 
  importReactfrom'react';
 
  import{connect}from"react-redux";
 
  importTodoItemfrom"./TodoItem.js";
 
  classTodoBdextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <div>
 
  //{JSON.stringify(this.props.todos)}
 
  {
 
  this.props.todos.map(item=>{
 
  //return<divkey={item.id}>{item.title}</div>
 
  return<TodoItemkey={item.id}item={item}></TodoItem>
 
  })
 
  }
 
  </div>
 
  );
 
  }
 
  }
 
  //connect目的是问“天”要数据,要通天。
 
  //“通天”是比喻,就是说要问store要数据
 
  exportdefaultconnect(
 
  (state)=>{
 
  return{
 
  todos:state.todos
 
  }
 
  }
 
  )(TodoBd);
 
  TodoItem.js
 
  importReactfrom'react';
 
  exportdefaultclassTodoItemextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <divclassName="todoItem">
 
  <inputtype="checkbox"checked={this.props.item.done}/>
 
  <span>{this.props.item.title}</span>
 
  <button>删除</button>
 
  </div>
 
  );
 
  }
 
  }
 
  TodoHd.js增加待办事项
 
  importReactfrom'react';
 
  import{connect}from"react-redux";
 
  classTodoHdextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <div>
 
  <inputtype="text"ref="titleTxt"/>
 
  <buttononClick={()=>{
 
  this.props.addTodo(this.refs.titleTxt.value);
 
  this.refs.titleTxt.value="";
 
  }}>添加</button>
 
  </div>
 
  );
 
  }
 
  }
 
  //这个组件要通天的目的不是要数据,而是改变数据
 
  exportdefaultconnect(
 
  null,
 
  (dispatch)=>{
 
  return{
 
  addTodo(title){
 
  dispatch({"type":"ADDTODO",title})
 
  }
 
  }
 
  }
 
  )(TodoHd);
 
  reducers/index.js写可预测状态
 
  constinitObj={
 
  "todos":[
 
  ...
 
  ],
 
  }
 
  exportdefault(state=initObj,action)=>{
 
  if(action.type=="ADDTODO"){
 
  return{
 
  ...state,
 
  "todos":[
 
  ...state.todos,
 
  {
 
  "id":state.todos.reduce((a,b)=>{
 
  returnb.id>a?b.id:a;
 
  },0)+1,
 
  "title":action.title,
 
  "done":false
 
  }
 
  ]
 
  }
 
  }
 
  returnstate;
 
  }
 
  TodoFt.js
 
  importReactfrom"react";
 
  import{connect}from"react-redux";
 
  classTodoFtextendsReact.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>
 
  }
 
  }
 
  exportdefaultconnect(
 
  (state)=>{
 
  return{
 
  todos:state.todos
 
  }
 
  }
 
  )(TodoFt)
 
  示例代码
 
  因为TodoItem这个组件是不通天的,所以TodoItem是不能自己独立dispatch到store的。
 
  此时就需要TodoBd帮助,因为TodoBd是通天。
 
  这是套路:所有被循环语句map出来的组件,一律不通天,数据父亲给,改变store的能力父亲给。
 
  TodoBd.js组件引入了TodoItem.js组件,因为TodoItem组件是被map出来的,所以信息要传给每一个TodoItem,而不是让TodoItem自己通天拿数据。
 
  TodoBd.js
 
  importReactfrom'react';
 
  import{connect}from"react-redux";
 
  importTodoItemfrom"./TodoItem.js";
 
  classTodoBdextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  //根据全局的show属性来决定当前todos数组
 
  if(this.props.show=="ALL"){
 
  vartodos=this.props.todos;
 
  }elseif(this.props.show=="ONLYDONE"){
 
  vartodos=this.props.todos.filter(item=>item.done);
 
  }elseif(this.props.show=="ONLYUNDONE"){
 
  vartodos=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>
 
  );
 
  }
 
  }
 
  exportdefaultconnect(
 
  (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
 
  importReactfrom'react';
 
  exportdefaultclassTodoItemextendsReact.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})
 
  }}
 
  />
 
  :
 
  <spanonDoubleClick={()=>{this.setState({"onEdit":true})}}>
 
  {title}
 
  </span>
 
  }
 
  <buttononClick={()=>{this.props.delTodo(id)}}>删除</button>
 
  </div>
 
  );
 
  }
 
  }
 
  index.js
 
  constinitObj={
 
  "todos":[
 
  ...
 
  ],
 
  "show":"ALL"//ALL、ONLYDONE、ONLYUNDONE
 
  }
 
  exportdefault(state=initObj,action)=>{
 
  if(action.type=="ADDTODO"){
 
  ...
 
  }elseif(action.type=="DELTODO"){
 
  return{
 
  ...state,
 
  "todos":state.todos.filter(item=>item.id!=action.id)
 
  }
 
  }elseif(action.type=="CHANGETODO"){
 
  return{
 
  ...state,
 
  "todos":state.todos.map(item=>{
 
  //如果遍历到的item项和传入的aciton的id项不一样,此时返回原item
 
  if(item.id!=action.id)returnitem;
 
  //否则返回修改之后的item
 
  return{
 
  ...item,
 
  [action.k]:action.v
 
  }
 
  })
 
  }
 
  }elseif(action.type=="CHANGESHOW"){
 
  return{
 
  ...state,
 
  "show":action.show
 
  }
 
  }
 
  returnstate;
 
  }
 
  TodoFt.js
 
  importReactfrom'react';
 
  import{connect}from"react-redux";
 
  importclassnamesfrom"classnames";
 
  classTodoFtextendsReact.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>
 
  <buttonclassName={classnames({"cur":this.props.show=='ALL'})}
 
  onClick={()=>{this.props.changeShow('ALL')}}>查看全部
 
  </button>
 
  <buttonclassName={classnames({"cur":this.props.show=='ONLYDONE'})}
 
  onClick={()=>{this.props.changeShow('ONLYDONE')}}>仅看已做
 
  </button>
 
  <buttonclassName={classnames({"cur":this.props.show=='ONLYUNDONE'})}
 
  onClick={()=>{this.props.changeShow('ONLYUNDONE')}}>仅看未做
 
  </button>
 
  </p>
 
  </div>
 
  );
 
  }
 
  }
 
  exportdefaultconnect(
 
  (state)=>{
 
  return{
 
  todos:state.todos,
 
  show:state.show
 
  }
 
  },
 
  (dispatch)=>{
 
  return{
 
  changeShow(show){
 
  dispatch({"type":"CHANGESHOW",show})
 
  }
 
  }
 
  }
 
  )(TodoFt);
 
  四、logger插件
 
  redux-logger用来辅助开发。
 
  npminstall--saveredux-logger
 
  改变main.js:
 
  importReactfrom"react";
 
  importReactDOMfrom"react-dom";
 
  import{createStore,applyMiddleware}from"redux";
 
  import{Provider}from"react-redux";
 
  importloggerfrom"redux-logger";
 
  importAppfrom"./App.js";
 
  //引入reducer
 
  importreducerfrom"./reducers/index.js";
 
  //创建store
 
  conststore=createStore(reducer,applyMiddleware(logger));
 
  ReactDOM.render(
 
  <Providerstore={store}>
 
  <App></App>
 
  </Provider>
 
  ,
 
  document.getElementById("app-container")
 
  );
 
  也可以使用redux-devtools这个插件。
 
  npminstall--save-devredux-devtools
 
  npminstall--save-devredux-devtools-log-monitor
 
  npminstall--save-devredux-devtools-dock-monitor
 
  npminstall--save-devredux-devtools-chart-monitor
 
  文档:
 
  https://github.com/reduxjs/redux-devtools
 
  https://github.com/reduxjs/redux-devtools/blob/master/docs/Walkthrough.md
 
  五、combineReducers和bindActionCreators
 
  一个网页的应用程序可能是多个reducer,合并为一个reducer,比如counter和todo的reducer。
 
  Redux提供的combineReducers方法,用于reducer的拆分,只要定义各个子reducer函数,然后用这个方法,将它们合成一个大的reducer。
 
  Redux提供的bindActionCreators方法,用于通过dispatch将action包裹起来,这条可以通过bindActionCreators创建的方法,直接调用dispatch(action)“隐式调用”。
 
  5.1combineReducers
 
  reducers/counter.js就是一个普通的纯函数:
 
  exportdefault(state={"v":10},action)=>{
 
  returnstate;
 
  }
 
  reducers/todo.js提供的数据:
 
  constinitObj={
 
  "todos":[
 
  {"id":1,"title":"吃饭","done":false},
 
  {"id":2,"title":"睡觉","done":false},
 
  {"id":3,"title":"打豆豆","done":true}
 
  ]
 
  };
 
  exportdefault(state=initObj,action)=>{
 
  returnstate
 
  }
 
  示例代码
 
  reducers/index.js要用redux提供的combineReducers来进行智能合并
 
  import{combineReducers}from"redux";
 
  importcounterfrom"./counter.js";
 
  importtodosfrom"./todos.js";
 
  //暴露合并的reducer
 
  exportdefaultcombineReducers({
 
  counter,
 
  todos
 
  })
 
  main.js
 
  importReactfrom"react";
 
  importReactDOMfrom"react-dom";
 
  import{createStore}from"redux";
 
  import{Provider}from"react-redux";
 
  importAppfrom"./containers/App.js";
 
  //引入reducer
 
  importreducerfrom"./reducers";
 
  //创建Reduxstore来存放应用的状态。
 
  conststore=createStore(reducer);
 
  ReactDOM.render(
 
  <Providerstore={store}>
 
  <App></App>
 
  </Provider>,
 
  document.getElementById("app")
 
  );
 
  containers/App.js组件使用数据
 
  importReactfrom'react';
 
  import{connect}from"react-redux";
 
  classAppextendsReact.Component{
 
  constructor(){
 
  super();
 
  }
 
  render(){
 
  return(
 
  <div>
 
  <h1>{this.props.v}</h1>
 
  </div>
 
  );
 
  }
 
  }
 
  exportdefaultconnect(
 
  ({counter})=>({
 
  v:counter.v
 
  })
 
  )(App);
 
  components/TodoList/index.js组件
 
  importReactfrom"react";
 
  import{connect}from"react-redux";
 
  classTodoListextendsReact.Component{
 
  constructor(){
 
  super();
 
  }
 
  render(){
 
  return(
 
  <div>
 
  <h1>我是TodoList</h1>
 
  {
 
  this.props.todos.map(item=>{
 
  return<pkey={item.id}>{item.title}</p>
 
  })
 
  }
 
  </div>
 
  )
 
  }
 
  };
 
  exportdefaultconnect(
 
  ({todos:{todos}})=>{
 
  return{
 
  todos
 
  }
 
  }
 
  )(TodoList)
 
  containers/App.js引入组件:
 
  importReactfrom'react';
 
  importTodoListfrom"../components/todolist/index.js";
 
  importCounterfrom"../components/counter/index.js";
 
  exportdefaultclassAppextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <div>
 
  <Counter></Counter>
 
  <TodoList></TodoList>
 
  </div>
 
  );
 
  }
 
  }
 
  5.2bindActionCreators
 
  bindActionCreators会将action和dispatch绑定并返回一个对象,这个对象会作为props的一部分传入组件中。
 
  bindActionCreators主要作用:一般情况下,可以通过Provider将store通过React的connext属性向下传递,bindActionCreators的唯一用处就是需要传递actioncreater到子组件,并且该子组件并没有接收到父组件上传递的store和dispatch。
 
  官方的文件夹结构:https://github.com/reduxjs/redux/tree/master/examples/todomvc/src
 
  actions/counterActions.js:新建actions文件夹存放type
 
  //我们把return一个action的函数叫做“actioncreator”
 
  //所以这个文件向外暴露了几个动作
 
  exportconstadd=()=>({type:"ADD"})
 
  exportconstminus=()=>({type:"MINUS"})
 
  exportconstcheng=()=>({type:"CHENG"})
 
  exportconstchu=()=>({type:"CHU"})
 
  counter/index.js计数器组件
 
  importReactfrom'react';
 
  import{bindActionCreators}from"redux";
 
  import{connect}from"react-redux";
 
  import*ascounterActionsfrom"../../actions/counterActions.js";
 
  classCounterextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <div>
 
  <h1>Counter:{this.props.v}</h1>
 
  <buttononClick={()=>{this.props.counterActions.add()}}>加</button>
 
  <buttononClick={()=>{this.props.counterActions.minus()}}>减</button>
 
  <buttononClick={()=>{this.props.counterActions.cheng()}}>乘</button>
 
  <buttononClick={()=>{this.props.counterActions.chu()}}>除</button>
 
  </div>
 
  );
 
  }
 
  }
 
  exportdefaultconnect(
 
  ({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";
 
  exportdefault(state={"v":0},action)=>{
 
  if(action.type=="ADD"){
 
  return{
 
  ...state,
 
  "v":state.v+1
 
  }
 
  }elseif(action.type=="MINUS"){
 
  return{
 
  ...state,
 
  "v":state.v-1
 
  }
 
  }elseif(action.type=="CHENG"){
 
  return{
 
  ...state,
 
  "v":state.v*2
 
  }
 
  }elseif(action.type=="CHU"){
 
  return{
 
  ...state,
 
  "v":state.v/2
 
  }
 
  }
 
  returnstate;
 
  }
 
  示例代码
 
  todolist/index.js
 
  importReactfrom'react';
 
  importTodoHdfrom"./TodoHd.js";
 
  importTodoBdfrom"./TodoBd.js";
 
  exportdefaultclassTodoListextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <div>
 
  <h1>TodoList</h1>
 
  <TodoHd></TodoHd>
 
  <TodoBd></TodoBd>
 
  </div>
 
  );
 
  }
 
  }
 
  TodoHd.js
 
  importReactfrom'react';
 
  import{bindActionCreators}from"redux";
 
  import{connect}from"react-redux";
 
  import*astodoActionsfrom"../../actions/todoActions.js";
 
  classTodoHdextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <div>
 
  <inputtype="text"ref="titleTxt"/>
 
  <buttononClick={()=>{this.props.todoActions.add(this.refs.titleTxt.value)}}
 
  >添加
 
  </button>
 
  </div>
 
  );
 
  }
 
  }
 
  exportdefaultconnect(
 
  null,
 
  (dispatch)=>({
 
  todoActions:bindActionCreators(todoActions,dispatch)
 
  })
 
  )(TodoHd);
 
  TodoBd.js
 
  importReactfrom'react';
 
  import{bindActionCreators}from"redux";
 
  import{connect}from"react-redux";
 
  import*astodoActionsfrom"../../actions/todoActions.js";
 
  classTodoBdextendsReact.Component{
 
  constructor(props){
 
  super(props);
 
  }
 
  render(){
 
  return(
 
  <div>
 
  {
 
  this.props.todos.map(item=>{
 
  return<pkey={item.id}>
 
  {item.title}
 
  <buttononClick={()=>{this.props.todoActions.del(item.id)}}>
 
  删除
 
  </button>
 
  </p>
 
  })
 
  }
 
  </div>
 
  );
 
  }
 
  }
 
  exportdefaultconnect(
 
  ({todo})=>({
 
  todos:todo.todos
 
  }),
 
  (dispatch)=>({
 
  todoActions:bindActionCreators(todoActions,dispatch)
 
  })
 
  )(TodoBd);
 
  为了防止action的type命名冲突,此时要单独存放在const文件夹中:
 
  app\constants\COUNTER.js
 
  exportconstADD="ADD_COUNTER";
 
  exportconstMINUS="MINUS_COUNTER";
 
  exportconstCHENG="CHENG_COUNTER";
 
  exportconstCHU="CHU_COUNTER";
 
  app\constants\TODO.js
 
  exportconstADD="ADD_TODO";
 
  exportconstDEL="DEL_TODO";
 
  然后就可以在以下文件中,引入以上常量,然后使用大写的常量替换type字符串
 
  lactions中的counterActions.js和todoActions.js
 
  lreducers中的todo.js和counter.js
 
  actions/TodoActions.js
 
  import{ADD,DEL}from"../constants/TODO.js";
 
  exportconstadd=(title)=>({"type":ADD,title});
 
  exportconstdel=(id)=>({"type":DEL,id});
 
  actions/counterActions.js:
 
  import{ADD,MINUS,CHENG,CHU}from"../constants/COUNTER.js";
 
  exportconstadd=()=>({"type":ADD});
 
  exportconstminus=()=>({"type":MINUS});
 
  exportconstcheng=()=>({"type":CHENG});
 
  exportconstchu=(n)=>({"type":CHU,n});
 
  reducers/todo.js
 
  import{ADD,DEL}from"../constants/TODO.js";
 
  constinitObj={
 
  "todos":[
 
  {"id":1,"title":"吃饭","done":false},
 
  {"id":2,"title":"睡觉","done":false},
 
  {"id":3,"title":"打豆豆","done":false}
 
  ]
 
  }
 
  exportdefault(state=initObj,action)=>{
 
  if(action.type==ADD){
 
  return{
 
  ...state,
 
  "todos":[
 
  ...state.todos,
 
  {
 
  "id":state.todos.reduce((a,b)=>{returnb.id>a?b.id:a},0)+1,
 
  "title":action.title,
 
  "done":action.done
 
  }
 
  ]
 
  }
 
  }elseif(action.type==DEL){
 
  return{
 
  ...state,
 
  "todos":state.todos.filter(item=>item.id!=action.id)
 
  }
 
  }
 
  returnstate;
 
  }
 
  reducers/counter.js
 
  import{ADD,MINUS,CHENG,CHU}from"../constants/COUNTER.js";
 
  exportdefault(state={"v":0},action)=>{
 
  if(action.type==ADD){
 
  return{
 
  ...state,
 
  "v":state.v+1
 
  }
 
  }elseif(action.type==MINUS){
 
  return{
 
  ...state,
 
  "v":state.v-1
 
  }
 
  }elseif(action.type==CHENG){
 
  return{
 
  ...state,
 
  "v":state.v*2
 
  }
 
  }elseif(action.type==CHU){
 
  return{
 
  ...state,
 
  "v":state.v/action.n
 
  }
 
  }
 
  returnstate;

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h56662.shtml