Redux入门

121 阅读3分钟

参考资料

Redux = Reducer + Flux

调试Redux:chrome插件Redux DevTools

纯函数

纯函数指的是,给定固定的输入,就一定会返回固定的输出(所以不能有ajax请求、new Date()和定时器)。而且不会有任何副作用(如不能修改store的内容,要先复制一份再去操作)。

Redux设计和使用的三项原则

  • store是唯一的

  • 只有store能够改变自己的内容

  • Reducer必须是纯函数

Redux的核心API

  • createStore: 创建store仓库
  • store.dispatch: 派发action,并传递给store
  • store.getState: 获取store中所有的内容
  • store.subscribe: 订阅store的改变

Redux 的工作流程

  • React Components是借阅人,要借书

  • Store是用来存储数据的. 相当于图书管理员

  • Action就是借阅人对图书管理员说的话(我要借xx书)

  • Reducers就是借阅记录,管理员在这里找书

用一个实际的例子来解释redux的工作流程:

当借阅人(React Components)要借书时,对管理员(Store)说”我要借xx书”(Action),管理员就在借阅记录(Reducers)上找出来。然后将书(state)给借阅人(React Components)。

Redux的实际工作流程

上图为流程图,下面为简单的代码实现逻辑

src/store/index.js  
	const store = createStore(reducer) // 2 Store依赖reducer存储和更新数据
	
src/store/reducer.js
	export defalut (state, action) => {
    return newState // 5 处理数据并return state给Store
  	return state // 1 初始化数据并return state给Store
  }

src/TodoList.js
	constructor(props) {
  	super(props)
    this.state = store.getState() // 3 获取数据
    store.subscribe(this.handleStoreChange) // 6 订阅store,监听store中state的变化
  }
	render() {
  	return (<button onClick={this.handleSubmit.bind(this)}><button>
  }
	handleSubmit() {
  	const action = {
  	type: '',
    value: ''
    }
    store.dispatch(action) // 4 修改数据并转发给Store
  }
	handleStoreChange() { // 7 监听到Store变化并更新组件
  	this.setState(store.getState())
  }
  1. 要想更新state中的数据,首先派发(dispatch)一个action,action通过dispatch方法把类型(type)和新数据传给store。

  2. store把之前的数据(previousState)和传过来的action转发给reducers函数。

  3. reducers接收previousState和action后进行数据处理,重新生成一个newState(原state只读不改),把newState作为返回值返回给store。

  4. store接收newState,将新数据替换原来的数据。

  5. react组件中观测(store.subscribe)到数据发生改变,会从store里面重新取数据(state)设置(setState)state,更新组件的内容,页面发生变化。

在项目中使用Redux

Installation
npm install --save redux

Store文件目录

  • src/store/actionCreators.js  统一管理action
  • src/store/actionTypes.js   action的type定义成常量利于定位错误
  • src/store/index.js
  • src/store/reducer.js 
  • src/index.js
  • src/TodoList.js

src

store

actionCreators.js

// src/store/actionCreators.js

import { CHANGE_INPUT_VALUE, BTN_SUBMIT, DELETE } from './actionType'

export const getInputChangeAction = (value) => ({
  type: CHANGE_INPUT_VALUE,
  value
})

export const getBtnSubmitAction = () => ({
  type: BTN_SUBMIT
})

export const getDeleteAction = (index) => ({
  type: DELETE,
  index
})

actionTypes.js

// src/store/actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value'
export const BTN_SUBMIT = 'btn_submit'
export const DELETE = 'delete'

index.js

// src/store/index.js

// 1 store仓库建立

import { createStore } from 'redux'
import reducer from './reducer' // 2 import reducer的数据

// redux devtools
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION_ && window.__REDUX_DEVTOOLS_EXTENSION_())

export default store // export store

reducer.js

// src/store/reducer.js
// 2 reducer建立 export出state
import { CHANGE_INPUT_VALUE, BTN_SUBMIT, DELETE } from './actionType'
const defaultState = { // store默认值
    inputValue: '',
    list:[]
}

// reducer 可以接收state 但是绝不能修改state
// 纯函数指的是,给定固定的输入,就一定会有固定的输出,而且不会有任何副作用
export default (state = defaultState, action) => { // 5 store接收组件传来的action解析,并修改store中的state
    if(action.type === CHANGE_INPUT_VALUE) {
        const newState = JSON.parse(JSON.stringify(state))
        newState.inputValue = action.value
        return newState
    }
    if(action.type === BTN_SUBMIT) {
        const newState = JSON.parse(JSON.stringify(state))
        newState.list.push(newState.inputValue)
        newState.inputValue = ''
        return newState
    }
    if(action.type === DELETE) {
        const newState = JSON.parse(JSON.stringify(state))
        newState.list.splice(action.index, 1)
        return newState
    }
    return state
}

index.js

// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import TodoList from './TodoList'

ReactDOM.render(<TodoList />, document.getElementById('root'))

TodoList.js

// src/TodoList.js
import React, { Component, Fragment } from 'react'
import store from './store' // 3 引入store供组件使用
import { getInputChangeAction, getBtnSubmitAction, getDeleteAction } from './store/actionCreators'
// import { CHANGE_INPUT_VALUE, BTN_SUBMIT, DELETE } from './store/actionType'

import 'antd/dist/antd.css'
import { Input, Button, List } from 'antd'

class TodoList extends Component {
  constructor(props) {
    super(props)
    this.state = store.getState() // 3 引入store赋值给组件的state
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleBtnClick = this.handleBtnClick.bind(this)
    this.handleStoreChange = this.handleStoreChange.bind(this)
    store.subscribe(this.handleStoreChange) // 6 订阅store,监听store中state的变化
  }
  render() {
    return (
      <Fragment>
        <div style={{marginTop: '10px', marginLeft: '10px'}}>
          <Input 
            placeholder='todo info' 
            style={{width: '300px', marginRight: '10px', marginBottom: '10px'}}
            value={this.state.inputValue} 
            onChange={this.handleInputChange}
          />
          <Button onClick={this.handleBtnClick} type="primary">提交</Button>
          <List
            style={{width: '300px'}}
            bordered
            dataSource={this.state.list}
            renderItem={(item, index) => (
              <List.Item onClick={this.handleDelete.bind(this, index)}>{item}</List.Item>
            )}
          />
        </div>
      </Fragment>
    )
  }
  handleInputChange(e) { // 4 组件的state值发生改变,存储到action,并通过store.dispatch(action)转发给store
    const action = getInputChangeAction(e.target.value)
    store.dispatch(action)
  }
  handleBtnClick() {
    const action = getBtnSubmitAction()
    store.dispatch(action)
  }
  handleDelete(index) {
    const action = getDeleteAction(index)
    store.dispatch(action)
  }
  handleStoreChange() { // 7 监听到store的变化,更新当前组件的state
    this.setState(store.getState())
  }
}

export default TodoList