Redux 和React 结合

  当Redux 和React 相接合,就是使用Redux进行状态管理,使用React 开发页面UI。相比传统的html, 使用React 开发页面,确实带来了很多好处,组件化,代码复用,但是和Redux 接合时,组件化却也带来了一定的问题,组件层层嵌套,有成千上百个,而store确只有一个,组件中怎么才能获取到store?  页面UI就是显示应用程序状态的,如果获取不到store中的state, 那就没法渲染内容了。还有一个问题,就是如果状态发生了变化,组件怎么做到实时监听,实时显示最新的状态?

  对于第一个问题,React组件中怎么获取到store,你可能想到了, 在整个应用程序的最外层组件中把store 作为props 层层向下传递,对于一个小程序,还可以接受, 但对于一个大型程序呢,不可能成千上百个组件中都写上store 属性吧。还有一个解决方案就是context,  把所有组件包含在一个context中,context 提供store 属性,这样就不用层层传递,且所有的组件都会获取到store.,方案可以一试

  对于第二个问题,组件内部想要实时显示最新的状态,那就要使用store.subscribe() 方法,在其里面注册监听函数,获取最新状态,然后注入到组件中,组件更新的方法,就是调用setState() 方法,那我们的每一个组件都变成了有状态的组件。那store.subsribe() 方法,什么时候注册监听函数,必须组件加载完就要注册,componentDidMounted 里调用store.subscribe().

  根据以上两点分析,尝试写一下代码,看不能能实现Redux和React 的接合,使用create-react-app 创建项目react-redux-demo,然后 cd react-redux-demo && npm i bootstrap redux --save,安装boostrap 和redux。 打开项目,在index.js 中引入boostrap.

import React from ‘react‘;
import ReactDOM from ‘react-dom‘;
import ‘./index.css‘;
import App from ‘./App‘;
import ‘bootstrap/dist/css/bootstrap.css‘; // 添加bootstrap 样式

ReactDOM.render(<App />, document.getElementById(‘root‘));

  还是最简单的加减counter 开始,点击add 加1, 点击minus 减1, 点击reset 重置。看一下Redux, 由于Redux 就是action, reducer, store,和React 一点关系都没有,所以完全把创建action,创建stroe的内容写成单独的文件,只暴露出React需要的东西给它调用就好了。React 需要store, 需要action, 因为它要dispatch action来改变状态。简单起见,把redux的有关内容都放到一个文件中,在src目录下新建一个文件redux.js

import {createStore} from ‘redux‘;// state
const initialState = { counter: 5 };

// action
const add = { type: ‘ADD‘ };
const minus = { type: ‘MINUS‘ };
const reset = { type: ‘RESET‘ };

// reducer
function counter(state = initialState, action) {
    switch(action.type) {
        case ‘ADD‘:
            return {
                ...state,
                counter: state.counter + 1
            }
        case ‘MINUS‘:
            return  {
                ...state,
                counter: state.counter - 1
            }
        case ‘RESET‘:
            return {
         ...state, 
         counter: 5
       };
        default:
            return state;
    }
}
// 创建store
const store = createStore(counter);

// export 出去store 和 action
export {store, add, minus, reset};

  现在就要写React,创建页面ui, 先不管交互,先把页面三个按钮和状态的显示画出来,在src下创建一个ThreeButton.js,

import React, { Component } from ‘react‘

export default class ThreeButton extends Component {
    render() {
        return (
            <div style={{textAlign: "center"}}>
                <h1 id="counter">0</h1>
                <button type="button" className="btn btn-primary" style={{marginRight: ‘10px‘}}>Add</button>
                <button type="button" className="btn btn-success" style={{marginRight: ‘10px‘}}>Minus</button>
                <button type="button" className="btn btn-danger">Reset</button>
            </div>
        )
    }
}

  然后在App.js中引入

import React from ‘react‘;
import ThreeButton from ‘./ThreeButton‘;

function App() {
  return (
    <ThreeButton></ThreeButton>
  );
}

export default App;

   准备实现React和Redux的接合,实现页面的交互。首先就是要把store 注入到React中,使用React 的context api. context使用的最开始,是使用createContext创建一个context,  在src 目录下新建一个storeContext.js

import React from ‘react‘;
const storeContext = React.createContext({store: {}})
export {storeContext};

  storeContext 有一个属性Provider, 它是一个组件,有一个value属性,提供真正的组件共享数据,这里就是Redux 创建的store 了。然后用Provider 把组件包起来,该组件和它的子组件都能够获取到共享数据,那就把App 包起来, 那就在index.js中把Redux的store和storeContext.js 引入

import React from ‘react‘;
import ReactDOM from ‘react-dom‘;
import ‘./index.css‘;
import App from ‘./App‘;
import ‘bootstrap/dist/css/bootstrap.css‘; // 添加bootstrap 样式

import { store } from ‘./redux‘; // 引入 store

 import { storeContext } from ‘./storeContext‘; // 引入storeContext

// 提供sotre 作为共享数据,App及其子组件都能获取到store
const Provider = <storeContext.Provider value={{store: store}}>
        <App />
    </storeContext.Provider>

ReactDOM.render(Provider, document.getElementById(‘root‘));

  那ThreeButton.js 就可以获取到store, 那具体是怎么获取到store的呢?首先还是引入storeContext, 然后在类中加一个静态属性contextType, 它赋值为storeContext, 然后组件中就可以使用this.context 获取到store 了。

import React, { Component } from ‘react‘;
import { storeContext } from ‘./storeContext‘; // 引入storeContext

export default class ThreeButton extends Component {
    static contextType = storeContext; // 加静态属性contextType, 赋值为storeContext

    componentDidMount() {
        let {store} = this.context; // this.context 获取到store
        console.log(store);
    }

    render() {
        return (
            <div style={{textAlign: "center"}}>
                <h1 id="counter">0</h1>
                <button type="button" className="btn btn-primary" style={{marginRight: ‘10px‘}}>Add</button>
                <button type="button" className="btn btn-success" style={{marginRight: ‘10px‘}}>Minus</button>
                <button type="button" className="btn btn-danger">Reset</button>
            </div>
        )
    }
}

  可以看到控制台打印出了store. 组件终于获取到store, 那就要从store中获取state,注入组件中,那组件就要声明一个状态 allState 来接收store中的state, 同时在componentDidMounted 的时候,调用setState 给它赋值

  static contextType = storeContext; // 加静态属性contextType, 赋值为storeContext

    state = {
        allState: {}
    }

    componentDidMount() {
        let {store} = this.context; // this.context 获取到store
        this.setState({
            allState: store.getState()
        })
    }
    

  把h1 中0 改为从状态获取

 <h1 id="counter">{this.state.allState.counter}</h1>

  页面中显示为5,没有问题,表明从store中获取的状态没有问题。那就要给三个按钮添加click 事件了,dispatch action 来改变状态,那就添加三个函数。首先从redux.js中引入三个action , 然后声明三个函数dipatch action, 最后就是给按钮添加上click 事件。

import { add, minus, reset } from ‘./redux‘;
.....

 add = () => {
        let {store} = this.context;
        store.dispatch(add);
    }

    minus = () => {
        let {store} = this.context;
        store.dispatch(minus);
    }

    reset = () => {
        let {store} = this.context;
        store.dispatch(reset);
    }

...
 <button type="button" className="btn btn-primary" style={{marginRight: ‘10px‘}}
                    onClick={this.add}>Add</button>
                <button type="button" className="btn btn-success" style={{marginRight: ‘10px‘}}
                    onClick={this.minus}>Minus</button>
                <button type="button" className="btn btn-danger"
                    onClick={this.reset}>Reset</button>

  点击了按钮,页面的状态并没有刷新,那就是没有subscribe 监听状态的改变, 还是在componentDidMounted 页面里面调用store.subscribe,它的回调函数也很简单,就是获取状态,调用setState()

componentDidMount() {
        let {store} = this.context; // this.context 获取到store
        this.setState({
            allState: store.getState()
        })

        store.subscribe(() => {
            this.setState({
                allState: store.getState()
            })
        })
    }

  至此,react 和redux 算是接合成功了。整个threeButton.js 如下

import React, { Component } from ‘react‘;
import { storeContext } from ‘./storeContext‘; // 引入storeContext
import { add, minus, reset } from ‘./redux‘;

export default class ThreeButton extends Component {
    static contextType = storeContext; // 加静态属性contextType, 赋值为storeContext

    state = {
        allState: {}
    }

    componentDidMount() {
        let {store} = this.context; // this.context 获取到store
        this.setState({
            allState: store.getState()
        })

        store.subscribe(() => {
            this.setState({
                allState: store.getState()
            })
        })
    }

    add = () => {
        let {store} = this.context;
        store.dispatch(add);
    }

    minus = () => {
        let {store} = this.context;
        store.dispatch(minus);
    }

    reset = () => {
        let {store} = this.context;
        store.dispatch(reset);
    }
    render() {
        return (
            <div style={{textAlign: "center"}}>
                <h1 id="counter">{this.state.allState.counter}</h1>
                <button type="button" className="btn btn-primary" style={{marginRight: ‘10px‘}}
                    onClick={this.add}>Add</button>
                <button type="button" className="btn btn-success" style={{marginRight: ‘10px‘}}
                    onClick={this.minus}>Minus</button>
                <button type="button" className="btn btn-danger"
                    onClick={this.reset}>Reset</button>
            </div>
        )
    }
}

  现在回想一下组件中获取store, dipatch aciton ,和实现实时监听的步骤, 你会发现当我们再创建另外一个组件的时候,它也有好多相同的步骤 ,

  1 ,添加静态属性contextType,使我们整个组件都能够获取到store,  肯定相同

  2, 添加state来接受store中的state, 肯定相同。

  3,componentDidMounted 下获取state ,监听state, 肯定相同

  4, dispatch action, 这个几乎不相同,因为每一个组件触发的action 不同

  5,render state, 就是页面的ui, 这个也几乎不同。

  我们可以把这个组件分为三个部分,相同的部分不动,那不同的部分要怎么处理?对于不同的部分,通常都是使用函数,不同的部分通过传参的形式传递进来,那就要写一个函数,返回这个组件。由于action 和ui 是两个不同类型的东西,可以分为两种不同的参数,那这个函数接受action 返加一个函数,返加函数再接受一个ui 组件,再返回一个组件,这个组件包含相同的部分。相当于

function connect(action) {
    return function(Componnet) {
        return class extends React.componnet {
            // 相同的部分
            render() {
                return <Componnet  {...this.state}></Componnet>
            }
        }
    }
}

  只要把这个函数封装起来,以后直接调用这个函数,就实现了组件自动获取到store, 自动监听变化,我们只要写ui 和action,然后传递进去就可以了。这个函数其实第三方组件已经封装好了,那就是react-redux 库,它提供了一个connect方法, 看一下它的api, 最常用的就是下面的方式

connect(mapStateToProps, mapDispatchToProps)(MyComponent)。

  和我们自己写的connect 函数使用方法一致, 只不过它的第一个函数可以接受更多的参数,mapStateToProps, 把state 转化成props,因为connect 返回的组件中能够获取到store中的state, 它要把state 传递给myComponnet, 因为myCompoent 才是负责渲染ui, 对于myComponnet 来说,它就是props. 同理也适用于mapDispatchToProps, 在connect的组件它是能获取到store中的dispatch,  当传递给myComponent的时候,它就变成了Props.  再看一下这两个参数怎么使用,首先它们是函数,然后返回对象。 为什么要这样设计呢?只有函数,才能调用,才能通过参数把state和disptch  进行注入,返回对象,便于对象的合并,把所有对象进行合并,形成props 传递给myComponnet.

  对于mapStateToProps 来说,它接受一个state作为参数,返回一个对象,这个对象中的属性就可以在myComponet中使用props 进行获取并使用,值呢?就是 参数state中的属性,myComponent 组件要用到state中的哪个属性,就读取state中的哪个属性作为参数。

const mapStateToProps = state => ({
    counter: state.counter
})

  mapDispatchToProps,则相对麻烦一点,它接受一个dispatch 作为参数,返回的对象中属性也是可以在myComponet中使用props 进行获取并使用,值呢,是一个函数,参数可以接受也可以不接受,函数体则是dispatch action

const mapDispatchToProps = dispatch => ({
    add: () => dispatch({type: ‘ADD‘})
})

  React-Redux 除了connect 函数外,还提供了Provider 组件,和我们自定义的storeContext.Provider 一致,不过它的使用方式是直接提供属性,组件身上的属性都能被子组件获取到。npm i react-redux --save 使用react-redux 重写组件。

  首先把storeContext.js 文件去掉,然后在index.js中从React-Redux引入Provider 组件,包含App

import React from ‘react‘;
import ReactDOM from ‘react-dom‘;
import ‘./index.css‘;
import App from ‘./App‘;
import ‘bootstrap/dist/css/bootstrap.css‘; // 添加bootstrap 样式

import { store } from ‘./redux‘; // 引入 store
import { Provider } from ‘react-redux‘;

// 提供sotre 作为共享数据,App及其子组件都能获取到store
const ProviderWrapper = <Provider store={store}>
        <App />
    </Provider>

ReactDOM.render(ProviderWrapper, document.getElementById(‘root‘));

  然后ThreeButton.js 就要分为两部分了,一个部分是connect 函数中的第一部分connect(mapStateToProps, mapDispatchToProps), 主要的作用就是把store 获取,转化为props.

另一部分是connect的第二部分myComponent,  它呢,就是接受到props, 渲染组件。 在一个文件中也可以,分两个文件也没有问题。我们就在一个文件中写了,

import React from ‘react‘;
import { add, minus, reset } from ‘./redux‘;

 import { connect } from ‘react-redux‘;

// 纯渲染组件
function ThreeButton(props) {

    return (
        <div style={{textAlign: "center"}}>
            <h1 id="counter">{props.counter}</h1>
            <button type="button" className="btn btn-primary" style={{marginRight: ‘10px‘}}
                onClick={props.add}>Add</button>
            <button type="button" className="btn btn-success" style={{marginRight: ‘10px‘}}
                onClick={props.minus}>Minus</button>
            <button type="button" className="btn btn-danger"
                onClick={props.reset}>Reset</button>
        </div>
    )
}

// 把store中的state 转化为纯渲染组件props
const mapStateToProps = state => ({
    counter: state.counter
})

// 获取store中的dispatch,同时和action接合,组成纯渲染组件props,渲染组件中,直接调用对象的属性,就可以dispatch action 了
const mapDispatchToProps = dispatch => ({
    add: () => dispatch(add),
    minus: () => dispatch(minus),
    reset: () => dispatch(reset)
})

// connect 函数把它们接合起来,ThreeButton就可以使用props来使用mapStateToProps和mapDispatchToProps中返回的对象属性
// 同时返回一个组件,可以在父组件App.js 中直接调用
export default connect(
    mapStateToProps,
    mapDispatchToProps,
  )(ThreeButton)

原文地址:https://www.cnblogs.com/SamWeb/p/11337197.html

时间: 2024-10-29 05:00:42

Redux 和React 结合的相关文章

[Redux] Adding React Router to the Project

We will learn how to add React Router to a Redux project and make it render our root component. Install: npm install --save react-router import React from 'react'; import {Provider} from 'react-redux'; import {Router, Route} from 'react-router'; impo

Redux 管理React Native数据

现在让我们看看大致的流程: React 可以触发 Action,比如按钮点击按钮. Action 是对象,包含一个类型以及相关的数据,通过 Store 的 dispatch() 函数发送到 Store. Store 接收 Action 的数据并将其连同当前的 state 树(state 树是包含所有 state 的一种特殊的数据结构,是一个单一的对象)发给 Reducer. Reducer 是一个多个函数的合成函数(当然一般都是),它接收一个之前的 state 和一个 Action:并基于此 A

Redux和React

export app class Compo1 extends Component{ } Compo1.propType = { a:PropTypes.string, fn:PropTypes.func.isRequired}限制父类组件输入参数的属性isRequired 必输 生命周期 组件从诞生到消亡,程序提供了各个阶段:1.上树阶段 Mounting,按顺序调用下列方法:1)constructor ()构造器2)componentWillMount() 正在上树3)render () 显

Flux --&gt; Redux --&gt; Redux React 入门 基础实例使用

本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推荐使用 ES6+React+Webpack 的开发模式,但Webpack需要配置一些东西,你可以先略过,本文不需要Webpack基础 入门,只是一些基础概念和用法的整理,更完整的内容推荐去看看文档,英文,中文 (不过我个人认为,官方文档的例子相对来说太复杂了,很难让新手马上抓住重点) (官方的例子正

react+redux渲染性能优化原理

大家都知道,react的一个痛点就是非父子关系的组件之间的通信,其官方文档对此也并不避讳: For communication between two components that don't have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount(), unsubscribe in componentWillU

Redux管理你的React应用

使用Redux管理你的React应用 因为redux和react的版本更新的比较频繁,博客园这里用的redux版本是1.0.1,如果你关心最新版本的使用技巧,欢迎来我的Github查看(https://github.com/matthew-sun/blog/issues/18) ,我会在这里进行持续的更新和纠错. React是最好的前端库,因为其发源于世界上最好的后端语言框架. ---信仰 4.0 will likely be the last major release. Use Redux

理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux?

作者:Wang Namelos链接:https://www.zhihu.com/question/41312576/answer/90782136来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. . React有props和state: props意味着父级分发下来的属性,state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内部消化.理解这个是理解React和Redux的前提.2. 一般构建的R

React、Redux 和 Bootstrap

使用 React.Redux 和 Bootstrap 实现 Alert 今天,我们来学习使用 React.Redux 和 Bootstrap 实现Alert. 例子 这个例子实现了弹出不同类型信息的功能,这些信息默认会在5秒后消失,你也可以手动点击使其消失.如果你在服务端有信息要提示,还可以通过 Redux 的单一数据源传到客户端在渲染页面时显示出来. 源代码: https://github.com/lewis617/react-redux-tutorial/tree/master/r2-bs-

理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux?(转)

作者:Wang Namelos 链接:https://www.zhihu.com/question/41312576/answer/90782136来源:知乎 解答这个问题并不困难:唯一的要求是你熟悉React.不要光听别人描述名词,理解起来是很困难的.从需求出发,看看使用React需要什么:1. React有props和state: props意味着父级分发下来的属性,state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内