本文介绍redux的使用

  1. 安装

    cnpm install redux --save
    cnpm install react-redux --save
    cnpm install redux-devtools --save-dev
    如果你之前使用过vuex,我相信redux对于你来说就是易如反掌
    redux官网将的很杂很乱,但是实用的东西就那么点
    
  2. action

    action就是一个对象,用来描述你要修改store状态树中的数据
    {
        type: 'change_name',
        name: 'yejiawei'
    }
    type字段必须要有,这是约定
    
  3. action创建函数

    正常情况下,你需要给每一个action定义一个函数,从而方便调用
    export function changeName (value) {
        return {
            type: 'change_name',
            name: value
        }
    }
    
  4. Reducer

    Reducer的作用,就是将不同的action汇总,然后返回相应的state
    const initialState = {
        name: 'haha'
    }
    function firstDemo (state = initialState, action) {
        switch (action.type) {
            case "change_name":
                return Object.assign({},state,{
                    name: action.name
                })
            default: 
                return state
        }
    }
    返回值必须是全新的
    
  5. 拆分reducer

    实际开发中都是模块化的,有必要将不同模块的reducer分开
    import { combineReducers } from 'redux'
    combineReducers({firstDemo,firstDemo1})
    
  6. store

    创建store是非常简单的
    import { createStore } from 'redux'
    将上面创建的reducer当做参数传递即可
    let store = createStore(firstDemo)
    store.getState() // 获取store中的数据
    let unsubscribe = store.subscribe( () => {
        ...
    } ) // 监听器
    store.dispatch(changeName('yejiawei')) // 调用action修改store中的state
    unsubscribe() // 注销监听器
    
  7. 在react组件中使用redux

    下面我将列出,正常项目开发的结构
    index.js
        import React from 'react'
        import ReactDOM from 'react-dom'
        import { Provider } from 'react-redux'
        import store from './store.js'
        import App from './app.js'
        ReactDOM.render(
            <Provider store={store}> 
                <App />
            </Provider>,
            document.getElementById('root')
        )
        使用 Provider 组件传递store到react组件中
    app.js
        import React from 'react'
        import MyComponent1 from './component1.js'
        import { connect } from 'react-redux'
        class MyComponent extends React.Component {
            render () {
                return (
                    <div>
                        <MyComponent1 {...this.props}></MyComponent1>
                    </div>
                )
            }
        }
        function appWant(state) {
            return state
        }
        export default connect(appWant)(MyComponent)
        使用connect方法将react组件连接到redux中,接受一个回调函数,并且回调函数的参数就是store中的state
        ** 原则,使用connect方法尽量只在容器组件中使用,其余的子组件如果也想访问store,就通过props传递即可
    store.js
        import { createStore } from 'redux'
        const initialState = {
            message: 'yejiawei'
        }
        function firstApp (state = initialState, action) {
            switch (action.type) {
                case "change_message":
                    return Object.assign({},state,{
                    message: action.message
                    })
                default: 
                    return state
            }
        }
        let store = createStore(firstApp);
        export default store
        此文件专门用来管理和生成store的,同时还可以将reducer专门再生成一个reducer.js管理
    component1.js
        此组件代表类似的子组件
        import React from 'react'
        import { delayData } from './actions.js'
        class MyComponent extends React.Component {
            componentDidMount() {
                this.props.dispatch(changeMessage('我改变了'))
            }
            render() {
                return (
                    <div style={{"height": "200px","width": "200px","background": "red","position": "absolute","top": "100px", "left": 0}}>我是组件一{this.props.message}</div>
                )
            }
        }
        export default MyComponent
        在子组件中访问store中的state和dispatch通过props直接访问即可
    actions.js
        此文件专门用来处理redux中的action生成函数
        export function changeMessage (text) {
            return {
                type: 'change_message',
                message: text
            }
        }
    
  8. 在react组件中使用redux补充

    上面讲到的connect方法,还可以传递其他参数
    注意到我们传递给connect方法的参数是如下的这个函数
        function appWant(state) {
            return state
        }
        这个函数会在state改变的时候更新整个app组件,也就是说不管你在哪里dispatch了,那么整个app都会重新更新,性能损失
        所以可以选择只传递一部分state,如下
        function appWant(state,ownProps) {
            return {
                age: state.age
            }
        }
        然后在其他的组件中也调用connect方法,管理自己的state,而不是只通过props传递,这样可以提高性能
    另外也可以把action单独传递或者传递一部分,我不建议这样做,对性能没有任何提高,反而提升代码复杂度,直接使用dispatch简单清晰明了
        在app.js文件中改成如下代码
            import React from 'react'
            import MyComponent1 from './component1.js'
            import { connect } from 'react-redux'
            import { bindActionCreators } from 'redux';
            import { delayData } from './actions.js'
            class MyComponent extends React.Component {
                render () {
                    return (
                        <div>
                            <MyComponent1 {...this.props}></MyComponent1>
                        </div>
                    )
                }
            }
            function appWant(state) {
                return {
                    age: state.age
                }
            }
            function funcWant(dispatch) {
                return {
                    demo: bindActionCreators({delayData},dispatch)
                }
            }
            export default connect(appWant,funcWant)(MyComponent)
        然后再component1.js中,就不需要通过dispatch调用了
            this.props.demo.delayData('我又变了');
    
  9. 异步action

    上面讲的内容是同步的,也就是说dispatch方法调用后,store中的state立即发生改变
    那么,现在有一个需求是,dispatch的写法不发生任何改变,还可以进行异步操作
    如何操作?只需要将action返回一个函数,然后在函数里面进行异步处理就完事儿了
    要实现这个操作就要借助 redux-thunk-middleware 中间件
    安装 cnpm install --save redux-thunk
    然后将上面的store.js文件改成下面的
        import { createStore, applyMiddleware  } from 'redux' // 导入applyMiddleware方法用来使用中间件
        import thunkMiddleware from 'redux-thunk' // 导入中间件
        const initialState = {
            message: 'yejiawei'
        }
        function firstApp (state = initialState, action) {
            switch (action.type) {
            case "change_message":
                return Object.assign({},state,{
                    message: action.message
                })
            default: 
                return state
            }
        }
        let store = createStore(firstApp,applyMiddleware(thunkMiddleware)); // 将中间件应用于store中
        export default store
    然后再actions.js中添加一个异步action
        export function delayData (value) {
            return (dispatch) => {
                setTimeout( () => {
                    dispatch(changeMessage(value))
                },1000 )
            }
        }
    最后,直接在component1.js文件中直接调用即可
        import React from 'react'
        import { delayData } from './actions.js'
        class MyComponent extends React.Component {
            componentDidMount() {
                this.props.dispatch(delayData('我改变了'))
            }
            render() {
                return (
                    <div style={{"height": "200px","width": "200px","background": "red","position": "absolute","top": "100px", "left": 0}}>我是组件一{this.props.message}</div>
                )
            }
        }
        export default MyComponent
    
  10. 异步action的补充一

    上面在定义异步action时,在返回的函数里面传递了dispatch参数,其实它还支持如下的参数
    还是借助上面的例子
    传递getState获取store中的state
        export function delayData (value) {
            return (dispatch,getState) => {
                setTimeout( () => {
                    dispatch(changeMessage(value))
                    console.log(getState())
                },1000 )
            }
        }
    传递自定义参数
        在store中将创建store的代码改成
            let store = createStore(firstApp,applyMiddleware(thunkMiddleware.withExtraArgument( {a: 'aaa', b: 'bbb'} )));
        然后再actions.js中获取参数
            export function delayData (value) {
                return (dispatch, getState, {a,b}) => {
                    setTimeout( () => {
                        dispatch(changeMessage(value))
                        console.log(a,b)
                        console.log(getState())
                    },1000 )
                }
            }
    

相关文章: