脉冲云之react,redux,react-redux实战演练

node.js学习之react,redux,react-redux

学习要讲方法,最好的方法莫过于实操学习法。


学习室操项目名叫 脉冲云。脉冲云是一款为开发者和运维人员提供帮助的软件,简洁强大的任务看板,功能齐全的代码仓库,快速稳定的编译构建,简单方便的部署管理,轻松自如的集群管理,一整套流程让你的开发更轻松。


这个项目的前端界面全程使用了react和redux,刚开始觉得挺难懂,于是在学会之后做了一个demo记录一下。
先看目录结构:
src
|--index.js
|--actions
  |--index.js
|--components
  |--Additem.js
  |--App.js
  |--ItemList.js
|--reducers
  |--index.js


redux三大件:actions , reducers, store ,
Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过store.dispatch() 将 action 传到 store。


这是官方的解释,我个人的理解是 action就是定义一个事件,但不做任何逻辑上的操作,只是把你需要处理的数据和事件绑定到一起,然后等待store进行派发,也就是更新store。就好像定义了一个按钮的点击事件 ;
1
btn.addEventListener(MouseEvent.CLICK,onClickHandle);
这句代码实际上什么操作也没有做,只是告诉系统有MouseEvent.CLICK消息的时候应该做什么。


reducer Action 只是描述了有事情发生了这一事实,并没有指明应用如何更新 state。而这正是 reducer 要做的事情。


这是官方解释,我的理解是就像上面说的onClickHandle函数,就是在等待事件被触发的时候要执行的代码,当有action被派发出来的时候,store就会根据定义来找到这个函数去执行,然后改变store,从而引起react的重新渲染,最终使view发生改变。


Store 就是把action 和 reducer 联系到一起的对象。Store 有以下职责:


维持应用的 state;
提供 getState() 方法获取 state;
提供 dispatch(action) 方法更新 state;
通过 subscribe(listener) 注册监听器。
 Redux 应用只有一个单一的 store。当需要拆分处理数据的逻辑时,使用 reducer 组合 而不是创建多个 store。


我的理解是store就是一个事件监听器加事件派发器加状态记录器。他会根据被触发的action来找到对应的reducer。


我在写这个demo的时候是先定义了action,也就是会有哪些操作,然后根据这些action去写reducer,也就是每个事件应该怎么处理。最后写了界面,当然,也可以先写界面,根据界面定义action。
下面看看代码是怎么写的,很简单并且很简陋的一段代码,
src/actions/index.js:


"use strict";
 
export const ADD_ITEM = 'add_item';
export const DEL_ITEM = 'delete_item';
 
export function addItem(text){
  return {
    type:ADD_ITEM,
    text
  }
}
 
export function delItem(index){
  return {
    type:DEL_ITEM,
    index
  }
}


这里定义了两个事件,一个是添加记录,一个是删除记录,每个方法都是返回一个对象,对象中必须有type属性,这个type属性实际上才是真正的事件区分器,
然后additem中的text是要添加记录的内容,index是要删除记录的所引。到这,action就完了。
reducers/index.js:


"use strict";
 
import { combineReducers } from 'redux';
import { ADD_ITEM, DEL_ITEM } from '../actions/index';
 
const initalState = {
  items:[]
};
 
function aitems(state=[],action){
  switch (action.type){
    case ADD_ITEM:
      return state.concat([action.text]);
    case DEL_ITEM:
      state.splice(action.index,1);
      return [...state];
    default:
      return state;
  }
}
 
let todos=combineReducers({
  items:aitems
});
export default todos;




这个文件里的内容就是根据action做的操作,通过type来区分发生了什么事,当应该添加一条记录的时候,就把text内容保存到数组中并返回,
这里有几点需要说明:
1,initalState{} 初始state,里面有一个属性items,数组类型,当然可以叫别的名字。
2,aitems的第一个参数state=[],这里为什么会让state赋值一个数组类型呢?不应该是object么?这是 因为在给todos赋值那一句,items:aitems,我对这点的理解是当触发事件的时候,redux会自动遍历todos,然后把state和combineReducers中传入的对象进行比较,遇到同名属性,就把state中的同名属性值传给combineReducers中的同名属性所对应的函数,有点绕!!!!
3,aitems的第二个参数action,你想的没错,就是刚才我们定义的 src/actions/index.js中的方法所   返回的对象。
4,combineReducers函数是一个很有个性的函数,不多说了。


src/index.js:


"use strict";
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import  App from './components/App';
import todos from './reducers/index';
 
let store = createStore(todos);
 
let element = document.getElementById('app');
render(
  <Provider store={store}>
    <App />
    </Provider>,
  element
);




这个要说的不太多,因为每个讲react和redux有关的文章里大部分会有这一段,主要是为了把react 和redux 通过 react-redux连接起来。
下面看组件:
src/components/App.js:


"use strict";
 
import React from 'react';
import {connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import  * as actions from '../actions/index';
import AddItem from './AddItem';
import ItemList from './ItemList';
 
class App extends React.Component{
  render(){
    console.log("app");
    return <div>
      <AddItem onAddClick={(text) => this.props.actions.addItem(text)} />
      <ItemList items={this.props.items} onDelClick={index=>this.props.actions.delItem(index)}/>
      </div>
  }
}
export default connect(({items})=>({items}),(dispatch) => ({actions:bindActionCreators(actions,dispatch)}))(App);


代码很简单,一看就能明白,唯一复杂一点的就是最后一句:


export default connect(({items})=>({items}),(dispatch) => ({actions:bindActionCreators(actions,dispatch)}))(App);


connect接收两个对象,第一个对象是store中的state,也可以是一部分,会变成绑定view的props属性,第二个对象是actions,也会变成当前view的props属性,这样就可以直接调用同名的actions,会自动派发对应的事件。connect函数执行过后会返回一个函数,再把当前的view传进去,就可以把当前的view,state,action绑定到一起,这样就能在当前的view中使用state的数据和派发事件了。


然后还有两个组件:
src/components/Additem.js:


"use strict";
import React from 'react';
 
export default class AddItem extends React.Component{
  static propTypes = {
    onAddClick:React.PropTypes.func
  };
  _clickHandle(e){
    console.log("additemclick");
    this.props.onAddClick(this.refs.itemText.value.trim());
  }
  render(){
    console.log("additem");
    return <div>
        <input type="text" ref="itemText"/><br/>
        <button onClick={this._clickHandle.bind(this)}>add</button>
      </div>
  }
}


这就是普通的react组件了,从父级接收一些参数来使用,或者接收一些函数来实现在子组件中调用父组件的函数。
src/components/ItemList.js:


"use strict";
 
import React from 'react';
 
export default class ItemList extends React.Component{
  static propTypes={
    items:React.PropTypes.array.isRequired,
    onDelClick:React.PropTypes.func
  };
  _onDelClick(index){
    console.log("itemlistclick",index);
    this.props.onDelClick(index);
  }
  render(){
    console.log("itemlist");
    console.log(this.props.items);
    return <ul>
      {
        this.props.items.map((value,index) =>
          <li>{index},{value}<button onClick={this._onDelClick.bind(this,index)}>del</button></li>
          )}
    </ul>
  }
}


这个,同上,不再多做解释。
恩。demo到这里,主要代码就完了,再来梳理一遍:
1,src/index.js把所有的包关联起来。
2,定义actions,定义reducers。
3,把state和actions绑定到App.js上。
4,从App.js中把state和actions分别传进子组件。
5,子组件中有操作,就会调用actions.
6,actions被触发,store调用对应的reducers.
7,reducers被调用,修改state.
8,react检测到state被修改,重新渲染组件,改变views。
恩。又跨近了一步。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值