写在后面
"待办事项列表"这个例子是redux文档举荐的,然而对于新人来说,官网文档和网上博客的相干解释都不是很好,没有思路剖析到代码实现整个个过程,这令我在学习的时候十分头疼。看着不分文件、不梳理数据流向、不解释代码思路的各种文章,我决定本人写一篇redux入门实例——简略的待办事项列表。
github
成果展现
开始之前
redux的根本准则
- 整个利用的
state
被贮存一个object tree
中,并且这个object tree
只存在于惟一的store
中。 - 惟一扭转
state
的办法是通过dispatch
触发action
,action
是一个形容批改用意的一般对象,例如:{ type: add, value }
。 - 当
store
收到action
后,开始在reducer
中计算,最初返回一个新的state
。 - Redux 入门教程, 官网文档
命令行
create-react-app redux-todolistcnpm i redux react-redux -S
目录构造
筹备
应用Provider
在根组件外用provider
包裹,能够让所有组件都能拿到state
。
// App.jsimport React from 'react';import { Provider } from 'react-redux';import AddTodo from './containers/addtodo'; //留神援用的地位import ShowList from './containers/showlist'; //留神援用的地位import Filter from './containers/filter' //留神援用的地位import store from './redux/store' //留神援用的地位function App() { return ( <Provider store = {store}> <AddTodo /> <ShowList /> <Filter /> </Provider> );}export default App;
触发Reducer主动执行
理论利用中,dispatch办法须要触发 reducer
的主动执行,从而对state
进行批改。为此,store
须要晓得 reducer
函数,做法就是在生成 store
的时候,将 reducer
传入createStore
办法。
// redux/storeimport {createStore} from 'redux'import reducers from './reducers/reducers'// createStore承受 reducers 作为参数,生成一个新的Store。// 当前每当dispatch发送过去一个新的 action,就会主动调用reducers(多个reducer的组合),失去新的 State。const store = createStore(reducers)export default store
增加事项
UI组件AddTodo
新建AddTodo
组件,当点击增加时,将输入框内的值value
通过调用props
接管到的addTodoText
办法,传递到containers
下的容器组件addtodo
中。
//components/addtodo/AddTodoimport React, { Component } from 'react';class AddTodo extends Component { handleAdd() { if(this.refs.inputText.value) { this.props.addTodoText(this.refs.inputText.value) // 调用承受到的addTodoText办法 // 这个办法会在containers下的容器组件addtodo中与组件AddToDo连贯 this.refs.inputText.value = '' } } render() { return ( <div> <input type="text" ref="inputText" /> <button onClick={this.handleAdd.bind(this)}>增加</button> </div> ); }}export default AddTodo;
容器组件addtodo
connect
办法会将UI组件AddTodo
进行包装,并增加业务逻辑:输出逻辑mapStateToProps
、输入逻辑mapDispatchToProps
,最初失去一个容器组件 addtodo
。这里只用到了输入逻辑(用户在组件上的操作如何变为action对象,点击的‘增加事项’会从这里传出去)。
// containers/addtodoimport AddTodo from '../components/addtodo/AddTodo';import { connect } from 'react-redux'import { addTodo } from '../redux/actions'// 这是一个重点,肯定要了解const mapDispatchToProps = (dispatch) => { return { addTodoText: (text)=> { //console.log('传递胜利',text); dispatch(addTodo(text)) // dispatch 会收回action-addTodo // 在redux/actions下存储了多个action } }}export default connect(null, mapDispatchToProps)(AddTodo)
// redux/actionsimport * as actionTypes from './actionTypes'export function addTodo(text) { return { type: actionTypes.ADD, text, completed: false} // 在actionTypes 下存储了多个用来形容批改用意的参数}
// redux/actionTypesexport const ADD = "ADD"// 增加事件
触发reducer
在容器组件addtodo
中触发了dispatch
并传递一个新的action
,主动调用reducers
中的todolist
。
// reducers/todolist function NewList(state = [], action) { //调用时的state内容是上次增加实现后的内容 //新内容在action中 switch(action.type){ case 'ADD': state = [{text: action.text, completed: action.completed}, ...state] return state default: return state }}export default NewList
// reducers/reducersimport { combineReducers } from 'redux'import NewList from './todolist'export default combineReducers({ NewList, // 有多个reducer要在这里引入})
获取reducer解决实现后的数据
这里将会应用UI组件ShowList
的容器组件showlist
,showlist
通过输出逻辑 mapStateToProps
获取reducer
解决实现后的state
,并将其映射到UI组件ShowList
的props
中。
// containers/showlist// 只有容器组件能力获取stateimport { connect } from 'react-redux';import ShowList from '../components/showlist/ShowList';const mapStateToProps = (state) => { return { list: state.NewList }}export default connect (mapStateToProps, null)(ShowList)
组件ShowList渲染数据
state
通过容器组件的传递,可在UI组件的this.props
中获取。
import React, { Component } from 'react';class ShowList extends Component { render() { let { list } = this.props //终于拿到点击增加后的事项 return ( <ul> { list.map((item, index) => ( <li key={index}> {item.text} </li> )) } </ul> ); }}export default ShowList;
实现事项
实现:点击事项,呈现删除线,示意已实现
UI组件ShowList
为每条事项增加点击事件,将点击的事项id
传给容器组件上的dispatch
,从而触发reducer
进行批改。
class ShowList extends Component { handleDone(index) { return () => { this.props.completedThing(index) } } render() { let { list } = this.props return ( <ul> { list.map((item, index) => ( <li onClick={this.handleDone(index)} key={index} className={item.completed ? 'line-through' : ''} ref='node'> // 在css文件中更改款式 {item.text} </li> )) } </ul> ); }}export default ShowList;
容器组件showlist
通过UI组件的触发,在mapDispatchToProps
中发动dispatch
申请(与增加事项类似)。
// containers/showlistimport { connect } from 'react-redux';import ShowList from '../components/showlist/ShowList';import { completed } from '../redux/actions' // 引入actionconst mapStateToProps = (state) => { return {list: state.NewList} // 之前写过的}const mapDispatchToProps=(dispatch)=>{ return { completedThing:(index)=>{ // console.log('传递胜利',index); dispatch(completed(index)) } }}export default connect (mapStateToProps, mapDispatchToProps)(ShowList)
// actionsexport function completed(index) { return { type: actionTypes.DONE, index} //将点击的事项的id传给reduce}// actionTypes// 实现事件export const DONE = 'DONE'
触发reducer
同样调用reducers
中的todolist
。
// reducers/todolist function NewList(state = [], action) { ...... case 'DONE': return (() => { state = state.slice(0) state[action.index].completed = !state[action.index].completed; // 批改事项中的complete参数 再返回数据 return state })() default: return state }}export default NewList
获取和渲染
批改过state
,UI组件ShowList
会从新渲染,相干方法不扭转。
筛选事项
UI组件Filter
增加三个按钮的点击事件,别离对应容器组件上的办法。
// components/filter/Filterimport React, { Component } from 'react';class Filter extends Component { handleAll() { this.props.renderAll() } handleActive() { this.props.renderActive() } handleGone() { this.props.renderGone() } render() { return ( <div> <button onClick={this.handleAll.bind(this)}>全副</button> <button onClick={this.handleActive.bind(this)}>未实现</button> <button onClick={this.handleGone.bind(this)}>已实现</button> </div> ); }}export default Filter;
容器组件filter
通过UI组件的触发,在mapDispatchToProps
中发动dispatch
申请。
import { connect } from 'react-redux';import Filter from '../components/filter/Filter';import { selectAll, selectActive, selectGone } from '../redux/actions'const mapDispatchToProps = (dispatch) => { return { renderAll: () => { //console.log('加载全副'); dispatch(selectAll()) }, renderActive: () => { //console.log('加载未实现'); dispatch(selectActive()) }, renderGone: () => { //console.log('加载已实现'); dispatch(selectGone()) } }}export default connect(null, mapDispatchToProps)(Filter)
// actionsexport function selectAll() { return { type: actionTypes.ALL } //留神这里传递的是点击的按钮参数 ‘ALL’}export function selectActive() { return { type: actionTypes.ACTIVE }}export function selectGone() { return { type: actionTypes.GONE }}// actionTypes// 加载全副事件export const ALL = 'ALL'// 加载未实现事件export const ACTIVE = 'ACTIVE'// 加载已实现事件export const GONE = 'GONE'
触发reducer
调用reducers
下的filter
,返回对应的参数放在 FilterTtpe
中。
// reducers/filterfunction FilterType(state, action) { switch(action.type) { case 'ACTIVE': return 'ACTIVE' case 'GONE': return 'GONE' default: return 'ALL' // 默认点击‘全副’,加载全副事项 }}export default FilterType
获取和渲染
在容器组件showlist
中通过接管到的NewList
和FilterType
,对list
进行筛选,返回给UI组件筛选实现后的新表。UI组件ShowList
从新渲染。因为在点击筛选按钮的过程中没有增加新的事项,所以state
中NewList
始终是最初一次增加实现后的内容。
import { connect } from 'react-redux';import ShowList from '../components/showlist/ShowList';import { completed } from '../redux/actions'const mapStateToProps = (state) => { //console.log(state.NewList); let fileList = [] switch(state.FilterType) { case 'ACTIVE': fileList = state.NewList.filter(item => item.completed === false) return { list: fileList} case 'GONE': fileList = state.NewList.filter(item => item.completed === true) return { list: fileList} default: return { list: state.NewList} }}const mapDispatchToProps=(dispatch)=>{ return { completedThing:(index)=>{ //console.log('传递胜利',index); dispatch(completed(index)) } }}export default connect (mapStateToProps, mapDispatchToProps)(ShowList)
总结
components
文件夹下的UI组件只负责渲染数据,不负责业务逻辑;参数都由this.props
提供,不应用this.state
。containers
文件夹下的容器组件负责数据管理和业务逻辑,次要通过mapStateToProps, mapDispatchToProps
来获取或解决数据。react-redux
提供的connect
办法用于生成容器组件。mapStateToProps
负责输出逻辑,将reducer
返回的state
映射到UI组件的props
中。mapDispatchToProps
负责输入逻辑,将用户的反馈参数映射在action
中,并通过发动dispatch
来传递给reducer
进行批改。