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地址


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

查看所有标签

猜你喜欢:

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

SQL基础教程

SQL基础教程

MICK / 孙淼、罗勇 / 人民邮电出版社 / 2017-6-1 / CNY 79.00

本书是畅销书《SQL基础教程》第2版,介绍了关系数据库以及用来操作关系数据库的SQL语言的使用方法。书中通过丰富的图示、大量示例程序和详实的操作步骤说明,让读者循序渐进地掌握SQL的基础知识和使用技巧,切实提高编程能力。每章结尾设置有练习题,帮助读者检验对各章内容的理解程度。另外,本书还将重要知识点总结为“法则”,方便读者随时查阅。第2版除了将示例程序更新为对应新版本的DB的SQL之外,还新增了一......一起来看看 《SQL基础教程》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

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

Markdown 在线编辑器