react、react-router、redux 也许是最佳小实践2
上一篇:react、react-router、redux 也许是最佳小实践1 加入 reduxReact 在组件之间流通数据.更确切的说,这被叫做“单向数据流”——数据沿着一个方向从父组件流到子组件。由于这个特性,对于没有父子关系的两个组件之间的数据交流就变得不是那么显而易见。这里 Redux 就排上用场了。Redux提供了一个解决方案,通过将应用程序所有的状态都存储在一个地方,叫做“store”。然后组件就可以“dispatch”状态的改变给这个store,而不是直接跟另外的组件交流。所有的组件都应该意识到状态的改变可以“subscribe”给store。如下图: 原理讲完,下面开始加入代码。 开始之前,需要先用 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'} }); 上面的程序干了些什么呢:
*在这个例子里reducer实际上被调用了两次 —— 一次是在创建store的时候,一次是在分派action之后。 当store被创建之后,Redux立即调用了所有的reducer,并且将它们的返回值作为初始状态。第一次调用reducer传递了一个 undefined 给state。经过reducer内部的代码处理之后返回了一个空数组给这个store的state作为开始。 所有的 在这个例子中,reducer第二次的调用发生在分派之后。记住,一个被分派的action描述了一个改变状态的意图,而且通常携带有数据用来更新状态。这一次, 正式redux登场在
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 了,在
export const ADD = 'ADD'; export const DELECT = 'DELECT'; export const SHOW = 'SHOW';
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 }; }
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({},{ planlist: newstate });; // 显示、隐藏弹出层 case types.SHOW: return Object.assign({},{ show: action.show }); } return state; } export default planReducer; 在redux 下面再创建
import { combineReducers } from 'redux'; // Reducers import planlist from './planlist'; // Combine Reducers var reducers = combineReducers({ planlist: planlist }); export default reducers;
import { createStore } from 'redux'; import reducers from './reducers.js'; const store = createStore(reducers); export default store; 这会我们的 store 就完全的创建好了,下面就是把 store 跟我们的组件,完全的结合起来。这就用到 react-redux 的 connect 模块。 然后在,
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
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,都是用这个模块连接
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);
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: '',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地址 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |