写在后面

"待办事项列表"这个例子是redux文档举荐的,然而对于新人来说,官网文档和网上博客的相干解释都不是很好,没有思路剖析到代码实现整个个过程,这令我在学习的时候十分头疼。看着不分文件、不梳理数据流向、不解释代码思路的各种文章,我决定本人写一篇redux入门实例——简略的待办事项列表。
github

成果展现

开始之前

redux的根本准则

  • 整个利用的state被贮存一个object tree中,并且这个object tree只存在于惟一的store中。
  • 惟一扭转state的办法是通过dispatch触发actionaction是一个形容批改用意的一般对象,例如:{ 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的容器组件showlistshowlist通过输出逻辑 mapStateToProps获取reducer解决实现后的state,并将其映射到UI组件ShowListprops中。

//  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中通过接管到的NewListFilterType,对list进行筛选,返回给UI组件筛选实现后的新表。UI组件ShowList从新渲染。因为在点击筛选按钮的过程中没有增加新的事项,所以stateNewList始终是最初一次增加实现后的内容。

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进行批改。