react、react-router、redux 也许是最佳小实践2

栏目: 服务器 · 发布时间: 7年前

内容简介:react、react-router、redux 也许是最佳小实践2

上一篇: react、react-router、redux 也许是最佳小实践1

加入 redux

React 在组件之间流通数据.更确切的说,这被叫做“单向数据流”——数据沿着一个方向从父组件流到子组件。由于这个特性,对于没有父子关系的两个组件之间的数据交流就变得不是那么显而易见。这里 Redux 就排上用场了。Redux提供了一个解决方案,通过将应用程序所有的状态都存储在一个地方,叫做“store”。然后组件就可以“dispatch”状态的改变给这个store,而不是直接跟另外的组件交流。所有的组件都应该意识到状态的改变可以“subscribe”给store。如下图:

react、react-router、redux 也许是最佳小实践2

原理讲完,下面开始加入代码。

先看看一个小例子。

开始之前,需要先用 Redux.createStore() 创建一个store,然后将所有的reducer作为参数传递进去,让我们看一下这个只传递了一个reducer的小例子:

var userReducer = function(state, action) {
  if (state === undefined) {
    state = [];
  }
  if (action.type === 'ADD_USER') {
    state.push(action.user);
  }
  return state;
}

var store = Redux.createStore(userReducer);

store.dispatch({
  type: 'ADD_USER',
  user: {name: 'xiaoming'}
});

上面的程序干了些什么呢:

  1. 这个store只由一个reducer创建。

  2. 这个reducer 初始化状态的时候使用了一个空数组 。*

  3. 在被分派的这个action里面使用了新的user对象。

  4. 这个reducer将这个新的user对象附加到state上,并将它返回,用来更新store。

*在这个例子里reducer实际上被调用了两次 —— 一次是在创建store的时候,一次是在分派action之后。

当store被创建之后,Redux立即调用了所有的reducer,并且将它们的返回值作为初始状态。第一次调用reducer传递了一个 undefined 给state。经过reducer内部的代码处理之后返回了一个空数组给这个store的state作为开始。

所有的 reducer 在每次 action 被分派之后都会被调用。因为 reducer 返回的状态将会成为新的状态存储在store中,所以 Redux 总是希望所有的 reducer 都要返回一个状态。

在这个例子中,reducer第二次的调用发生在分派之后。记住,一个被分派的action描述了一个改变状态的意图,而且通常携带有数据用来更新状态。这一次, Redux 将当前的状态(仍旧是空数组)和 action 对象一起传递给了 reducer 。这个 action 对象,现在有了一个值为1 ADD_USERtype 属性, 让 reducer 知道怎样改变状态。

正式redux登场

src 下面创建一个 reduxactionsdata (存放一些初始数据)文件夹,然后在 data 文件夹下面创建一个 db.js ,这个文件写上一些初始的数据:

src/data/db.js

const data = [
    {
        id: 1,
        title: '明天要去打酱油',
        content: '系呀系呀我们一起打酱油'
    },
    {
        id: 2,
        title: '周末去书吧读书',
        content: '书籍是人类进步的阶梯'
    },
    {
        id: 3,
        title: '备份一下数据库',
        content: '备份服务器的数据库,一般都是分开的,分布式数据库'
    },
    {
        id: 4,
        title: '周五记得把被子洗了',
        content: '洗杯子被子被子被子'
    },
    {
        id: 5,
        title: '计划五',
        content: '计划五内容'
    }
]

export default data

好了,初始的数据我们有了,下面就是创建 store 了,在 redux 文件夹下面,创建一个 planlist.js 文件,这个文件就是操作 store 的动作 action 集合处理的数据,这时候我们会去 action 文件夹下面新建, action-type.jsplan.js ,代码如下:

src/action/action-type.js

export const ADD = 'ADD';
export const DELECT = 'DELECT';
export const SHOW = 'SHOW';

src/action/plan.js

import * as types from './action-type.js';
// 添加计划
export function addPlan(item) {
  return {
    type: types.ADD,
    item
  };
}
// 删除计划
export function deletePlan(id) {
  return {
    type: types.DELECT,
    id
  };
}
// 显示隐藏弹层
export function show(show) {
  return {
    type: types.SHOW,
    show
  };
}

action 我们都定义好了现在我们就可以改变 store 了。写好我们的 reducer

src/redux/planlist.js

import * as types from '../actions/action-type.js';
import data from '../data/db.js'
const initialState = {
  show: false, // 是否显示弹出
  planlist: data // 初始的计划表
};

const planReducer = function(state = initialState, action) {
    let list = state.planlist;
  switch(action.type) {
    // 添加计划
    case types.ADD:
        list.push(action.item);
      return Object.assign({}, state, { planlist: list });
    // 删除计划
    case types.DELECT:
      let newstate = list.filter((item) => item.id != action.id);
      return Object.assign({}, state, { planlist: newstate });;
     // 显示、隐藏弹出层
     case types.SHOW:
         return Object.assign({}, state, { show: action.show });
  }
  return state;

}

export default planReducer;

在redux 下面再创建 reducers.jsstore.js

src/redux/reducers.js

import { combineReducers } from 'redux';

// Reducers
import planlist from './planlist';

// Combine Reducers
var reducers = combineReducers({
    planlist: planlist
});

export default reducers;

src/redux/store.js

import { createStore } from 'redux';
import reducers from './reducers.js';

const store = createStore(reducers);
export default store;

这会我们的 store 就完全的创建好了,下面就是把 store 跟我们的组件,完全的结合起来。这就用到 react-redux 的 connect 模块。

这个东西 就是把组件跟 store 连接起来的模块。

然后在, App.js 加入我们的。 store

src/App.js

import React, { Component } from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom'
// 引入 store
import { Provider, connect } from 'react-redux';
import store from './redux/store.js'
import logo from './logo.svg'
import Plan from './components/plan.js'
import Home from './components/home.js'
import Popup from './components/pupop.js'
import TestRouter from './components/testrouter.js'
import Detail from './components/detail.js'
import './App.css'
import './components/comment.css'
import createHistory from 'history/createBrowserHistory'
const history = createHistory()
class App extends Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
        // store的挂载
       <Provider store={store}>
        <div className="App">
            <div className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h2 className='App-title'>Welcome to React Plan</h2>
            </div>
            <div>
              <Router history = {history}>
                 <div className="contentBox">
                    <ul className="nav">
                      <li><Link to="/">首页</Link></li>
                      <li><Link to="/plan">计划表</Link></li>
                      <li><Link to="/test">二级路由</Link></li>
                    </ul>
                    <div className="content"> 
                      <Route exact path="/" component={Home}/>
                      <Route path="/plan" component={Plan}/>
                      <Route path="/test" component={TestRouter}/>
                      <Route path="/detail/:id" component={Detail}/>
                    </div>
                </div>
              </Router>
            </div>
            <Popup/>
        </div>
      </Provider>
    );
  }
}

export default App

然后在 plan.js连接 store

src/component/plant.js

import React, { Component } from 'react'
import { connect } from 'react-redux';
import store from '../redux/store.js';
// 引入 定义的 action
import {show, deletePlan} from '../actions/plan.js';

class Plan extends Component {
  constructor(props) {
      super(props);
  }
  // 显示弹出
  show () {
    let b = this.props.planlist.show;
    store.dispatch(show(!b));
  }
  // 删除计划
  delete (id) {
      store.dispatch(deletePlan(id));
  }
  // js 跳转路由
  detail (id) {
      this.props.history.push(`/detail/${id}`)
  }
    render () {
        return (
            <div>
                <div className="plant">
                    <h3>计划表</h3>
                    <p onClick={this.show.bind(this)}>添加计划</p>
                </div>
                <table className="planlist">
                    <thead>
                        <tr>
                            <th>标题</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody>
                        {
                            this.props.planlist.planlist.map((item, index) => {
                                return (
                                    <tr key={index}>
                                        <td className="plan-title" onClick={this.detail.bind(this, item.id)}>{item.title}</td>
                                        <td className="plan-delect" onClick={this.delete.bind(this, item.id)}>删除</td>
                                    </tr>
                                )
                            })
                        }
                    </tbody>
                </table>
            </div>
        )
    }
}

const mapStateToProps = function(store) {
  return {
    planlist: store.planlist
  };
};
// 连接 store,作为 props
export default connect(mapStateToProps)(Plan);

同理下面的 js,都是用这个模块连接

src/component/detail.js

import React, { Component } from 'react'
import { connect } from 'react-redux';
import store from '../redux/store.js';


class Detail extends Component {
    constructor(props) {
        super(props);
        // 根据路由 id 跟 store 做过滤
        let item = props.planlist.planlist.filter((data) => data.id == props.match.params.id)
        console.log(item)
        this.state = {
            plan: item[0]
        }
    }
    render() {
        return (
            <div style={{padding: '20px'}}>
                <h3>计划详情</h3>
                <p>id: {this.state.plan.id}</p>
                <p>标题: {this.state.plan.title}</p>
                <p>内容: {this.state.plan.content}</p>
            </div>

        )
    }
}


const mapStateToProps = function(store) {
  return {
    planlist: store.planlist
  };
};
// 连接 tore 和组件
export default connect(mapStateToProps)(Detail);

src/component/popup.js

import React, { Component } from 'react'
import { connect } from 'react-redux';
import store from '../redux/store.js';
import {show, addPlan} from '../actions/plan.js';

class Pupop extends Component{
  constructor (props) {
    super(props)
    this.state = {
      id: '',
      title: '1',
      content: '1'
    }
  }
  // 取消按钮操作
  close () {
    let b = this.props.planlist.show;
    this.setState({
      id: '',
      title: '',
      content: ''
    })
    store.dispatch(show(!b));
  }
  // 输入框事件
  handleChage (str, e) {
    this.setState({
      id: Math.ceil(Math.random()*10000),
      [str]: e.target.value
    })
  }
  // 确认操作
  conform () {
    store.dispatch(addPlan(this.state));
    this.setState({
      id: '',
      title: '',
      content: ''
    })
    this.close();
  }

  render() {
    let self = this;
    return (
      <section className="popup" style={this.props.planlist.show ? {} : {display: 'none'}}>
        <div className="pbox">
          <span className="close" onClick={this.close.bind(this)}>X</span>
          <div>
            <h4>计划标题</h4>
            <input onChange={this.handleChage.bind(this, 'title')} value={this.state.title} placeholder="请输入计划标题"/>
          </div>
          <div>
            <h4>计划内容</h4>
            <textarea onChange={this.handleChage.bind(this, 'content')} value={this.state.content} placeholder="请输入计划内容" rows="3"></textarea>
          </div>
          <div className="pBtn">
            <span onClick = {this.close.bind(this)}>取消</span>
            <span onClick = {this.conform.bind(this)}>确认</span>
          </div>
        </div>
      </section>
    )
  }
}

const mapStateToProps = function(store) {
  return {
    planlist: store.planlist
  };
};
// 连接 store和组件
export default connect(mapStateToProps)(Pupop);

完工。 github地址


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

淘宝、天猫网上开店速查速用一本通

淘宝、天猫网上开店速查速用一本通

刘珂 / 北京时代华文书局 / 2015-6 / 39.8

为了帮助众多的新手卖家掌握淘宝天猫网上开店、货源准备、店铺装修、商品拍摄、交易方法、营销推广以及售后服务等知识,本书作者根据自己多年网上开店心得,并结合了多名淘宝五皇冠店主和天猫旗舰店卖家的经验,精心策划编写了本书。 《淘宝、天猫网上开店速查速用一本通:开店、装修、运营、推广完全攻略》将目前最前沿、最流行的营销理念运用到淘宝天猫网上平台,所有技术都在实际应用获得显著效果,并且还在持续创造着惊......一起来看看 《淘宝、天猫网上开店速查速用一本通》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器