美文网首页
初探react-redux

初探react-redux

作者: 我叫傻先生 | 来源:发表于2018-07-06 15:37 被阅读0次

    什么是Redux

    • ReduxJavaScript 状态容器,它提供对状态的统一管理
    • Redux 除了和 React 一起用外,还支持其它界面库,如果需要在React中使用,需要单独安装react-redux
    • react-redux是一个能过够让ReduxReact项目中使用的第三方库

    Redux需要关注的有:Action,Reducer,Store

    Action

    Action主要用来管理一些分发出去的动作。
    Action 本质上是一个普通对象,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作,当应用规模越来越大时,建议使用单独的模块或文件来存放 action。如下:

    redux/action/index.tsx

    // 为了后期项目的可维护,把所有的action都写在单独文件
    const ACTION = {
        CHANGEVALUE: (obj:any) => {
            return {
                type: 'CHANGEVALUE',
                value: obj.value
            }
        },
        DECREASE: {
            type: 'DECREASE'
        },
        INCREASE: {
            type: 'INCREASE'
        }
    }
    
    export default ACTION
    
    Reducer

    Reducer主要指定了一些如何改变store中状态的方法:如下

    redux/reducers/index.tsx

    
    interface IInitState {
        count: number
    }
    interface IActionType {
        type: string,
        value: string
    }
    
    export default function reducers (state: IInitState , action: IActionType) {
        // 这里是根据传入动作的type,来对Redux中的state进行状态值修改 
        switch (action.type) {
            case 'INCREASE':
                return { count: ++state.count }
                break
            case 'DECREASE':
                return { count: --state.count }
                break
            case 'CHANGEVALUE':
                return {count: action.value}
                break
            default:
                return { count: state.count }
        }
    }
    
    
    Store

    Store就是将ActionReducer联系起来,使得可以通过Action来修改State

    主要API:

    • getState(),返回应用当前的 state 树。
    • dispatch(action),分发 action,这是触发 state 状态值改变
    • subscribe(listener),添加一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。你可以在回调函数里调用 getState() 来拿到当前 state。如果需要解绑这个变化监听器,执行 subscribe 返回的函数即可。
    • replaceReducer(nextReducer),替换 store 当前用来计算 state 的 reducer。

    redux/store/index.tsx

    import { createStore } from 'redux'
    import reducer from '../reducers'
    
    export const initState = {
        count: 0
    }
    // createStore 方法详见Redux API文档:http://www.redux.org.cn/docs/api/createStore.html
    const store = createStore(reducer, initState)
    
    export default store
    

    现在redux的工作已经完成了。我们可以在react文件中直接引入:import store from '../redux/store/index.tsx',然后使用Storeapi进行操作,但是这样并不会触发react组件的更新,因为他们之间缺少了映射关系,这时候就需要用到react-redux这个第三方库

    react-redux的使用

    Reduxreact-redux是不同的库,如果我们想要在react中使用,我们需要用到react-redux这个库,主要作用是将我们项目的状态集成到Redux中去管理

    需要关注的有:connect, Provider

    connect
    • connect方法,顾名思义,主要将容器组件和redux进行连接
    // 这里是把component这个组件进行连接,连接后可在props上查看映射关系
    const appContainer = connect(mapStateToProps, mapDispatchToProps)(component)
    
    • 他有两个参数:mapStateToPropsmapDispatchToProps,这两个参数的类型都是函数,具体作用如下:
    • mapStateToProps:将Reduxstate映射到组件的props
    • mapDispatchToProps:将Reduxaction映射到组件的props上,如下:

    redux/index.tsx:

    import { connect } from 'react-redux'
    import appContainer from '../views/Appcontainer'
    import ACTION from './action'
    
    function mapStateToProps(state: any) {
        // 这里将会把 strore 中的initState映射到组件的props上
        return {
            count: state.count
        }
    }
    
    function mapDispatchToProps(dispatch: (obj: any) => void) {
        return {
            // CHANGEVALUE, DECREASE, INCREASE这些函数名会映射到被连接的组件props上
            CHANGEVALUE: (obj:any) => {
                // 当连接redux的组件调用props上的CHANGEVALUE方法时,此处将分发对应的Action,Action在redux/action/index.tsx中维护
                // 找到reducers中相匹配的action,然后执行定义的方法
                dispatch(ACTION.CHANGEVALUE(obj))
            },
            DECREASE: () => {
                dispatch(ACTION.DECREASE)
            },
            INCREASE: () => {
                dispatch(ACTION.INCREASE)
            }
        }
    }
    // 此处将连接好的组件导出
    export const AppContainer = connect(mapStateToProps, mapDispatchToProps)(appContainer)
    
    

    如果需要现在使用导出的appContainer组件,还需要用到react-redux提供的Provider组件

    Provider

    它主要的功能有:

    • 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
    • 接收Redux的store作为props,并通过context传递给子组件

    App.tsx

    import * as React from 'react'
    import { Provider } from 'react-redux'
    import './App.css'
    import { AppContainer } from './redux'
    import store from './redux/store'
    
    class App extends React.Component {
        public render(): JSX.Element {
            return (
                <div>
                    <Provider store={store}>
                        <AppContainer/>
                    </Provider>
                </div>
            )
        }
    }
    
    export default App
    
    

    AppContainer.tsx

    
    
    import * as React from 'react'
    import { HashRouter as Router, Route } from 'react-router-dom'
    import Nav from '../../component/nav'
    import { loadable, route } from '../../routes'
    import Home from '../Home'
    
    interface IRouteItem {
        component: Promise<React.ComponentClass<any> | React.StatelessComponent<any> | { default: React.ComponentType<any> }>,
        path: string
    }
    
    export default class Appcontainer extends React.Component {
        public render ():JSX.Element {
            return (
                <Router>
                    <div>
                        <Nav />
                        <Route path='/' exact={true} component={Home}/>
                        {
                            route.map((item:IRouteItem, index:number) => {
                                return (
                                    <Route path={item.path} component={loadable(item.component)} key={index}/>
                                )
                            })
                        }
                    </div>            
                </Router>
            )
        }
    }
    

    验证react-redux已经将store映射到Appcontainer组件,如下:

    [图片上传失败...(image-aa2163-1530862614905)]

    可以看到Appcontainer组件的props已经有了之前在redux/index.tsx中定义的方法,现在就可以在Appcontainer中直接使用这些方法来修改store

    子孙组件调用

    在编写组件的时候我们为了可维护性,不会把组件都写在一个文件,所以肯定会有很多子组件或者孙子组件。一般来说,我们最外层的父组件用于逻辑处理,而子组件等用来做UI显示。

    但是在子孙组件中,获取store不能如同在Appcontainer组件中一样通过this.props.xx获取,现在了解的有三种方法:

    • 1.Appcontainer作为父组件,可以通过props一层一层的把方法和值传递给子组件或者孙子组件
    • 2.假设现在有子组件A,在redux/index.tsx使用connect方法将A组件与redux进行连接,再导出调用,这样子组件也有了和Appcontainer组件一样的操作store的方法和值
    • 3.在Appcontainer组件中定义context上下文,子组件通过this.context.xx.来获取

    这里我使用的是第三种方法:context

    Appcontainer.tsx中:

    
    import * as PropTypes from 'prop-types'
    import * as React from 'react'
    
    interface IRouteItem {
        component: Promise<React.ComponentClass<any> | React.StatelessComponent<any> | { default: React.ComponentType<any> }>,
        path: string
    }
    
    export default class Appcontainer extends React.Component<any, any>{
        // childContextTypes属性,声明给子孙组件提供的属性
        public static childContextTypes = {
            store: PropTypes.object
          }
        constructor (props:any) {
            super(props)
        }
        // 这个方法给子组件设置context的值,值为当前组件的props
        public getChildContext () {
            return {
                store: this.props
            }
        }
        public render ():JSX.Element {
            return (
               ...
            )
        }
    }
    

    子组件Counter中:

    import * as PropTypes from 'prop-types'
    
    export default class Counter extends React.Component<any, any> {
        // 声明静态属性 contextTypes 才能访问顶层组件定义的context,属性名字也需要和父组件一样,否则访问不到值
        public static contextTypes = {
            store: PropTypes.object
        }
        public handleDecreaseCB () {
            this.context.store.DECREASE()
        }
        public render () {
            return (
                <div className="container">
                    <div className="container_count">
                        <DecreaseBtn handleDecrease={this.handleDecreaseCB}/>
                        {this.props.children}
                    </div>
                </div>
            )    
        }
    }
    
    

    demo效果如下:

    image

    redux持久化

    效果图中有一个问题,就是刷新后,原来的store变为了0,变成之前初始化state的值,如果需要让数据持久化,则需要用到一个第三方包:redux-persist,配置也很简单:

    安装redux-persist

    yarn add redux-persist --save

    redux/store/index.tsx
    import { createStore } from 'redux'
    import * as persist from 'redux-persist'
    import storage from 'redux-persist/lib/storage'
    import reducer from '../reducers'
    
    const { persistStore, persistReducer } = persist
    
    const persistConfig = {
        key: 'root',
        storage,
      }
    const initState = {
        count: 0
    }
    const persistedReducer = persistReducer(persistConfig, reducer)
    const store = createStore(persistedReducer, initState)
    const persistor  = persistStore(store)
    
    export default {
        persistor,
        store
    }
    
    App.tsx
    import * as React from 'react'
    import { Provider } from 'react-redux'
    import { PersistGate } from 'redux-persist/integration/react'
    import './App.css'
    import { AppContainer } from './redux'
    import { persistor, store } from './redux/store'
    
    export default class App extends React.Component {
        public render(): JSX.Element {
            return (
                <div>
                    <Provider store={store}>
                        <PersistGate loading='加载中...' persistor={persistor}>
                            <AppContainer/>
                        </PersistGate>
                    </Provider>
                </div>
            )
        }
    }
    

    以上方法都是参照官方的例子实现,详见:gitHub,效果如下:

    image

    Demo地址

    相关文章

      网友评论

          本文标题:初探react-redux

          本文链接:https://www.haomeiwen.com/subject/ttcbuftx.html