React中的状态管理---Redux

栏目: IOS · Android · 发布时间: 5年前

内容简介:我们以todolist中增加代办事项这个功能为例:为了开发规范一点我们要采取reducer的分片写法,即一个类型数据一个模块index.js

我们以todolist中增加代办事项这个功能为例:

为了开发规范一点我们要采取reducer的分片写法,即一个类型数据一个模块

不使用react-redux书写规范

生产安装redux

yarn add redux复制代码

在src目录下建立store目录

建立index.js和reducer.js

index.js

import { createStore } from 'redux'

import reducer from './reducer'

const store = new createStore ( reducer )

export default store复制代码

reducer.js

import { combineReducers } from 'redux'

const reducer = combineReducers({
  
})

export default reducer复制代码

建立todos文件夹,依次建立

state.js

const state = {
    todos : [
        {
            id : 1,
            text : '睡觉'
        },
        {
            id : 2,
            text : '吃饭'
        },
        {
            id : 3,
            text : '打豆豆'
        }
    ]
}

export default state复制代码

type.js

export const ADD_TODOS = 'ADD_TODOS'复制代码

actionCreators.js

import * as type from './type'
import store from '../index'

const actionCreators = {
    addTodos( value ){
        let action = {
            type : type.ADD_TODOS,
            payload : value
        }
        store.dispatch( action )//相当于vue里的commit,把action发送给reducer
    }
}

export default actionCreators复制代码

reducer.js

import * as type from './type'
import state from './state'

const reducer = ( previousState = state,action) => {//将state赋值给previousState作为初始值
   let newState = {//结构出来给newState,以后对newState进行操作
       ...previousState
   }
   switch (action.type) {
       case type.ADD_TODOS:
            newState.todos.push({
            id : newState.todos.length + 1,
            text : action.payload
            })
            break;
        default:
            break;  
   }
   console.log(newState)
   return newState
}

export default reducer复制代码

这些完成后我们需要把todos文件夹下的reducer拿到src下的reducer使用,即src目录下的reducer.js文件改为这样

import { combineReducers } from 'redux'
import todos from './todos/reducer'

const reducer = combineReducers({
  todos
})

export default reducer复制代码

这样一个Redux就完成了,接着就是创建组件然后调用store来

React中的状态管理---Redux

src目录下建立pages目录,建立todos目录,下建立todo的组件

在组件中调用store里的方法,两个文件,TodoInput里是增加任务的,所以我们应当在TodoInput里调用activeCreators里的方法来执行任务

TodoInput.js

import React,{ Component,Fragment } from 'react'

import actionCreators from '../../store/todos/actionCreators'//引入actionCreators

class TodoInput extends Component {

    addTodos = (e) => {
        let keyCode = e.keyCode
        let value = e.target.value
        if ( keyCode === 13 ){
            actionCreators.addTodos(value)//调用actionCreators里的方法
        }
    }
    render () {
        return (
            <Fragment>
                <input onKeyUp={ this.addTodos } type="text"></input>
            </Fragment>
        )
    }
}

export default TodoInput复制代码

TodoContent.js

TodoContent里是数据的展示,这里有几个关键点,Redux里视图更新需要 store.subscribe 来完成,

知识点:

  1. 列表渲染的方法
  2. 怎么让actionCreators里的方法执行后视图更新
import React,{ Component,Fragment } from 'react'
import store from '../../store/index'

const Item = (props) => {
    return (
        <li>{ props.item.text }</li>
    )
}
class TodoContent extends Component {

    constructor () {
        super()
        this.state={
            todos : store.getState().todos.todos//reducer里的名称到todos里的state里的数据名称
        }
    }

    renderItem = () => {//最好的列表渲染方法
        return this.state.todos.map( (item,index)=>{
           return <Item item={ item } key={ item.id }></Item>
        })
    }

    componentDidMount(){//视图更新的方法
        store.subscribe(()=>{
            this.setState({
                todos : store.getState().todos.todos
            })
        })
    }
    render () {
        return (
            <Fragment>
                <ul>
                    { this.renderItem() }
                </ul>
            </Fragment>
        )
    }
}

export default TodoContent复制代码

index.js

import TodoInput from './TodoInput'
import TodoContent from './TodoContent'

export { TodoInput,TodoContent }复制代码

使用react-redux规范

使用react-redux涉及到的知识点

核心概念

容器组件(智能组件)、UI组件(木偶组件)

react-redux觉得如果一个组件想要使用到store中的数据或者是actionCreator的方法,我们都应该将其变化为容器组件包裹UI组件的样子

其中,容器组件负责连接store,将状态、方法传递给UI组件,UI组件从属性上取得这些api后进行使用

而且,不需要担心是容器组件可以根据UI组件来生成

核心API

Provider 、 connect(mapStateToProps,mapDistpatchToProps)

Provider负责将store相关的api,传入到内部所有的容器组件中

connect负责根据UI组件来生成容器组件

使用方法和细节

我们根据前面没使用react-redux的案例来改造

生产环境安装react-redux

yarn add react-redux复制代码

需要在组件的最外层套上Provider组件,并为其传入store

再在主入口文件用 Provider 组件包裹App组件 ,注意,Provider组件来自react-redux,还需要引入store赋值给Provider

import store from './store'

import { Provider } from 'react-redux'

ReactDOM.render(
  <Provider store = { store }>
    <App />
  </Provider>
, document.getElementById('root'));复制代码

利用connect将需要使用store相关api的组件变成容器组件嵌套UI组件的模式

connect方法的返回值是一个函数,这个函数接收到UI组件之后会返回一个容器组件,容器内部已经嵌套了UI组件

Provider组件会利用context上下文将自己属性中store传递给自己的子级组件,而容器组件会取得context上面的store相关的api

我们可以在connect函数中传入mapStateToProps/mapDispatchToProps参数来掌控容器组件给UI组件传递属性的过程

*mapStateToProps的作用*:

   将store中的state传递到UI组件的属性上
   值为一个函数,接收到的就是store中的state
   返回值是什么,UI组件的属性上就有什么
   并且,因为容器组件中已经做好了store.subscribe的处理,所以一旦store中的状态变化,容器组件就马上能得知,就会重新给UI组件传入新的数据


    *问题*: 坑  ? 我们的store中的数据在更新, 但是里面的容器组件中的 props 不更新

    分析: 先从组件上着手,查看我们书写的代码, 发现代码没有问题的, 这个时候我们就要去查看数据来源, 而数据来源来自于  reducer , 然后输出一下数据, 发现数据渲染了三次 , 有两次是一个组件在创建的时候, 数据从无到有, 还有一次是容器组件产生的 , 后面我们发送我们的new_state一直在重复初始化, 

    解决方案: 
        let newState = {...previousState}

mapDispatchToProps的作用:

   可以将能使用到dispatch的一些方法传递到UI组件上
   值为一个函数,接收到的就是store中的dispatch
   返回上面,UI组件的属性上就有什么
   这个时候actionCreator就变得很纯粹,只需要创建action,dispatch action的动作可以放到mapDispatchToProps的方法中,也就说,在mapDispatchToProps给UI组件传递的函数中将actionCreator创造好的action给dispatch走

但是这样的写法优点不舒服,因为actionCreator里有一个方法,还需要在mapDispatchToProps里再写一个方法

所以可以利用redux中的bindActionCreators将actionCreator中的方法直接放入到UI组件中并且将其返回的action给直接dispatch走


因为在组件中既要用到store中的状态,也要用到actionCreator的方法,导致这个组件引入了很多东西,其实我们可以将这些逻辑封装取出复制代码
import React,{ Component,Fragment } from 'react'
import actionCreators from '../../store/todos/actionCreators'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
 
const Item = (props) => {
    return (
        <li>{ props.item.text }</li>
    )
}
class TodoContent extends Component {

    renderItem = () => {
        return this.props.todos.todos.map( (item,index)=>{
           return <Item item={ item } key={ item.id }></Item>
        })
    }
    addTodos = (e) => {
        let keyCode = e.keyCode
        let value = e.target.value
        if ( keyCode === 13 ){
            this.props.addTodos(value)
        }
    }

    render () {
        console.log('render')

        return (
            <Fragment>
                 <input onKeyUp={ this.addTodos } type="text"></input>
                <ul>
                    { this.renderItem() }
                </ul>
            </Fragment>
        )
    }
}

// const mapStateToProps = ( state ) => {//第一种connect方法
//     // return state.todos
//     return {
//         todos : state.todos
//     }
// }

// const mapDispatchToProps = ( dispatch ) => {
//     console.log(dispatch)
//     return bindActionCreators ( actionCreators,dispatch )
// }

// export default connect(mapStateToProps,mapDispatchToProps)(TodoContent)

export default connect(//第二种connect方法
    state => state,
    dispatch => bindActionCreators(actionCreators,dispatch)
)(TodoContent)复制代码

注意:倘若方法和数据使用在不同的组件里,例如上面案例的输入框和列表展示不在同一个组件,那 么列表展示组件可以只引入

export default connect(
    state => state
)(TodoContent)复制代码

任务输入组件需要都使用

export default connect(
    state => state,
    dispatch => bindActionCreators(actionCreators,dispatch)
)(TodoInput)复制代码

再修改一下stor下的actionCreators.js的文件

import * as type from './type'

const actionCreators = {
    addTodos( value ){
        let action = {
            type : type.ADD_TODOS,
            payload : value
        }
        return action
    }
}

export default actionCreators复制代码

改动的就是由 store.dispatch(action)变成return action,因为我们dispatch功能现在交给组件去做了

react-redux的异步工具redux-thunk

安装redux-thunk

yarn add redux-thunk复制代码

使用详细

场景fetch请求一段数据(数据请求是异步的),将todo数据作为数据,根据上面的react-redux后来继续

redux-thunk一个重要的概念, 中间件(applyMiddleware)

我们主要修改store目录下的index.js

import { createStore,applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import reducer from './reducer'

const store = new createStore( reducer,applyMiddleware(thunk) )

export default store 复制代码

新的store下的index.js为这样

数据请求放在actionCreators.js里

这里的数据请求中的dispatch( action )的写法相当于return action,同样在使用中是会

import * as type from './type'
const actionCreators = {
    getData(){
        return dispatch =>{
            fetch('/data.json')
            .then(res=>res.json())
            .then(data=>{
                console.log('datad',data)
               let action = {
                   type : type.GET_DATA,
                   payload : data
               }
               dispatch( action )
            })
            .catch(error=>console.log(error)) 
        }   
    },
    addTodos(value){
        let action = {
            type : type.ADD_TODOS,
            payload : value
        }

        return action
    }
}

export default actionCreators复制代码

在reducer.js里修改下

import * as type from './type'
import state from './state'

const reducer = (previousState = state,action)=>{
    let newState = {
        ...previousState
    }
    switch(action.type){
        case type.GET_DATA:
            newState.todos=action.payload
            break;
        case type.ADD_TODOS:
            newState.todos.push({
                id : newState.todos.length + 1,
                text : action.payload
            })
            break;
        default:
            break;
    }
    console.log(newState)
    return newState
}

export default reducer复制代码

调用

TodoInput.js

import React,{ Component,Fragment } from 'react'
// import { connect } from 'react-redux'
// import  { bindActionCreators } from 'redux'
// import actionCreators from '../../store/todos/actionCreators'

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import actionCreators from '../../store/todos/actionCreators'

class TodoInput extends Component {
    addTodos = (e)=>{
        let keyCode = e.keyCode
        let value = e.target.value
        if (keyCode === 13){
            this.props.addTodos(value)
        }
    }

    getData=()=>{
        this.props.getData()
    }
    render() {
        return (
            <Fragment>
                <button onClick={ this.getData }>获取数据</button>
                <input onKeyUp={ this.addTodos } type="text" placeholder="请输入大粑粑"></input>
            </Fragment>
        )
    }
}

export default connect(
    state => state,//必须添加
    dispatch => bindActionCreators( actionCreators,dispatch )
)(TodoInput)复制代码

有个特殊的地方,如果一个组件只需要state,那么可以不要dispatch那一块,如果要dispatch,那么数据一定需要

TodoContent.js

import React,{ Component,Fragment } from 'react'

import { connect } from 'react-redux'

const Item = (props)=>{
    return (<li>{ props.item.text }</li>)
}
class TodoContent extends Component {

    renderItem = () => {
        return this.props.todos.todos.map( (item,index)=>{
            return <Item item= { item } key ={ item.id }></Item>
        })
    }
    
    render () {
        return (
            <Fragment>
                <ul>
                    { this.renderItem() }
                </ul>
            </Fragment>
        )
    }
}

// export default TodoContent

export default connect(
    state => state
)(TodoContent)复制代码

总结redux-thunk

它的使用方法及其简单:

  1. 安装redux-thunk
  2. 在创建store的时候使用中间件 import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' import reducer from './reducer' const store = createStore(reducer, applyMiddleware(thunk) )
  3. 这个时候,actionCreator的方法就可以返回一个能接收到dispatch的一个函数,我们可以在这个函数中进行异步操作之后,将actionCreator创建好的action给发送

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ordering Disorder

Ordering Disorder

Khoi Vinh / New Riders Press / 2010-12-03 / USD 29.99

The grid has long been an invaluable tool for creating order out of chaos for designers of all kinds—from city planners to architects to typesetters and graphic artists. In recent years, web designers......一起来看看 《Ordering Disorder》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具