Redux

redux是React的状态管理工具,它的工作流程如下

Redux适合的场景

  1. 用户的使用方式复杂
  2. 不同身份的用户有不同的使用方式
  3. 用户之间可以协作
  4. 与服务器大量交互
  5. view要从多个来源获取数据
安装redux
yarn add redux
基本概念
Store

保存数据的地方,整个应用只能有一个Store。创建Store的方法为createStore

import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
export default store
State

Store 包含所有的数据,State 是某一个时间点的数据集合,获取State的方法为store.getState()

this.state = store.getState()
Action

Action 用来改变Store中的数据,action是一个对象,约定必须有一个type属性,它表示action的名称,其他属性可以任意设置

handleInputChange(e) {
  const action = {
    // action 的类型
    type: CHANGE_INPUT_VALUE,
    // 需要传递的参数
    value: e.target.value
  }

  // 调用 dispatch 方法修改 store 的值
  store.dispatch(action)
}
Reducer

Reducer接受action修改Store的请求,然后把修改后的State返回给Store,Store用Reducer返回的值替换原来的值,因此只有Store能够改变自己的内容。

import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
const defaultState = {
  inputValue: '',
  list: []
}
// reducer 可以接收 state,但不能修改 state
const fn = (state = defaultState, action) => {
  if (action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state))
    newState.inputValue = action.value
    return newState
  }
  return state
}
export default fn

Reducer必须是一个纯函数,纯函数满足以下两点

  1. 给定输入输出是固定的
const fn = (state = defaultState, action) => {
  if (action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state))
    // inputValue 受到当前时间影响,所以fn不是纯函数
    newState.inputValue = new Date()
    return newState
  }
  return state
}
  1. 没有副作用
const fn = (state = defaultState, action) => {
  if (action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state))
    newState.inputValue = action.value
    // 对传入参数进行修改,因此是有副作用的
    state.inputValue = ''
    return newState
  }
  return state
}
Action Creator

一个项目可能会用到很多action,可以众多action集中到一个文件,便于进行管理

store/actionCreators.js

import { ADD_TODO_ITEM, CHANGE_INPUT_VALUE, DELETE_TODO_ITEM } from './actionTypes'

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

Redux高级

UI组件和容器组件

将React组件划分为UI组件和容器组件,UI组件负责渲染,容器组件负责处理逻辑,这样的划分可以避免组件的代码过于庞大,也让代码逻辑更加清晰。

TodoList.js

import React, { Component } from 'react'
import 'antd/dist/antd.css'

import store from './store'
import TodoListUI from './TodoListUI'
import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators'

class TodoList extends Component {
  constructor(props) {
    super(props)
    this.state = store.getState()
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleStoreChange = this.handleStoreChange.bind(this)
    this.handleBtnClick = this.handleBtnClick.bind(this)
    this.handleItemDelete = this.handleItemDelete.bind(this)
  }
  componentDidMount() {
    // 订阅 store 的变化,交给 handleStoreChange 处理
    store.subscribe(this.handleStoreChange)
  }
  componentWillUnmount() {
    store.unsubscribe(this.handleStoreChange) // 取消订阅,清理已注册的监听
  }

  render() {
    return (
      <TodoListUI
        inputValue={this.state.inputValue}
        handleInputChange={this.handleInputChange}
        handleBtnClick={this.handleBtnClick}
        list={this.state.list}
        handleItemDelete={this.handleItemDelete}
      />
    )
  }
  handleInputChange(e) {
    const action = getInputChangeAction(e.target.value)
    // 调用 dispatch 方法修改 store 的值
    store.dispatch(action)
  }
  handleStoreChange() {
    // 根据 store 更新 state
    this.setState(store.getState())
  }
  handleBtnClick() {
    const action = getAddItemAction()
    store.dispatch(action)
  }
  handleItemDelete(index) {
    const action = getDeleteItemAction(index)
    store.dispatch(action)
  }
}

export default TodoList

TodoListUI.js

import React, { Component } from 'react'
import { Input, Button, List } from 'antd'
class TodoListUI extends Component {
  render() {
    return (
      <div>
        <h1 style={{ textAlign: 'center' }}>TODO</h1>
        <div style={{ width: 'fit-content', margin: '50px auto' }}>
          <Input
            value={this.props.inputValue}
            placeholder="todo info"
            style={{ width: '300px', marginRight: '10px' }}
            onChange={this.props.handleInputChange}
          ></Input>
          <Button type="primary" onClick={this.props.handleBtnClick}>
            提交
          </Button>
        </div>
        <List
          bordered
          dataSource={this.props.list}
          renderItem={(item, index) => (
            // onClick 的监听函数需要接收参数,应该修改为函数的形式
            <List.Item
              onClick={(index) => {
                this.props.handleItemDelete(index)
              }}
              key={index}
            >
              {item}
            </List.Item>
          )}
        />
      </div>
    )
  }
}

export default TodoListUI

store/actionCreators.js

import { ADD_TODO_ITEM, CHANGE_INPUT_VALUE, DELETE_TODO_ITEM } from './actionTypes'

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

export const getAddItemAction = () => ({
  type: ADD_TODO_ITEM
})

export const getDeleteItemAction = (index) => ({
  type: DELETE_TODO_ITEM,
  index
})
无状态组件

无状态组件指的是只含有render函数的组件,一般可以将UI组件可以改写成无状态组件,可以提升一部分性能。

import React, { Component } from 'react'
import { Input, Button, List } from 'antd'

const TodoListUI = (props) => {
  return (
    <div>
      <h1 style={{ textAlign: 'center' }}>TODO</h1>
      <div style={{ width: 'fit-content', margin: '50px auto' }}>
        <Input
          value={props.inputValue}
          placeholder="todo info"
          style={{ width: '300px', marginRight: '10px' }}
          onChange={props.handleInputChange}
        ></Input>
        <Button type="primary" onClick={props.handleBtnClick}>
          提交
        </Button>
      </div>
      <List
        bordered
        dataSource={props.list}
        renderItem={(item, index) => (
          // onClick 的监听函数需要接收参数,应该修改为函数的形式
          <List.Item
            onClick={(index) => {
              props.handleItemDelete(index)
            }}
            key={index}
          >
            {item}
          </List.Item>
        )}
      />
    </div>
  )
}

export default TodoListUI
Redux发送异步请求获取数据
yarn add axios

TodoList.js

componentDidMount() {
  // 订阅 store 的变化,交给 handleStoreChange 处理
  store.subscribe(this.handleStoreChange)
  axios.get('http://localhost.charlesproxy.com:3000/api/todolist').then((res) => {
    const data = res.data
    const action = initListAction(data)
    store.dispatch(action)
  })
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容