通过Redux 架构明白我们领会到 Redux 架构的 store、action、reducers 这些基本概念和事情流程。我们也知道了 Redux 这种架构模式可以和其他的前端库组合使用,而 React-redux 正是把 Redux 这种架构模式和 React.js 连系起来的一个库。
Context
在 React 应用中,数据是通过 props 属性自上而下举行通报的。若是我们应用中的有许多组件需要共用同一个数据状态,可以通过状态提升的思绪,将配合状态提升到它们的公共父组件上面。然则我们知道这样做是异常繁琐的,而且代码也是难以维护的。这时会思量使用 Context,Context 提供了一个无需为每层组件手动添加 props,就能在组件树间举行数据通报的方式。也就是说在一个组件若是设置了 context,那么它的子组件都可以直接接见到内里的内容,而不用通过中心组件逐级通报,就像一个全局变量一样。
在 App -> Toolbar -> ThemedButton 使用 props 属性通报 theme,Toolbar 作为中心组件将 theme 从 App 组件 通报给 ThemedButton 组件。
class App extends React.Component { render() { return <Toolbar theme="dark" />; } } function Toolbar(props) { // Toolbar 组件接受一个分外的“theme”属性,然后通报给 ThemedButton 组件。 // 若是应用中每一个单独的按钮都需要知道 theme 的值,这会是件很贫苦的事, // 由于必须将这个值层层通报所有组件。 return ( <div> <ThemedButton theme={props.theme} /> </div> ); } class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} />; } }
使用 context,就可以制止通过中心元素通报 props 了
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入通报进组件树。 // 为当前的 theme 建立一个 context(“light”为默认值)。 const ThemeContext = React.createContext('light'); class App extends React.Component { render() { // 使用一个 Provider 来将当前的 theme 通报给以下的组件树。 // 无论多深,任何组件都能读取这个值。 // 在这个例子中,我们将 “dark” 作为当前的值通报下去。 return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } // 中心的组件再也不必指明往下通报 theme 了。 function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } class ThemedButton extends React.Component { // 指定 contextType 读取当前的 theme context。 // React 会往上找到最近的 theme Provider,然后使用它的值。 // 在这个例子中,当前的 theme 值为 “dark”。 static contextType = ThemeContext; render() { return <Button theme={this.context} />; } }
虽然解决了状态通报的问题却引入了 2 个新的问题。
1. 我们引入的 context 就像全局变量一样,内里的数据可以被子组件随意更改,可能会导致程序不能展望的运行。
2. context 极大地增强了组件之间的耦合性,使得组件的复用性变差,好比 ThemedButton 组件由于依赖了 context 的数据导致复用性变差。
我们知道,redux 不正是提供了治理共享状态的能力嘛,我们只要通过 redux 来治理 context 就可以啦,第一个问题就可以解决了。
Provider 组件
React-Redux 提供 Provider
组件,利用了 react 的 context 特征,将 store 放在了 context 内里,使得该组件下面的所有组件都能直接接见到 store。大致实现如下:
class Provider extends Component { // getChildContext 这个方式就是设置 context 的历程,它返回的工具就是 context,所有的子组件都可以接见到这个工具 getChildContext() { return { store: this.props.store }; } render() { return this.props.children; } } Provider.childContextTypes = { store: React.PropTypes.object }
那么我们可以这么使用,将 Provider 组件作为根组件将我们的应用包裹起来,那么整个应用的组件都可以接见到内里的数据了
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'; import { createStore } from 'redux'; import todoApp from './reducers'; import App from './components/App'; const store = createStore(todoApp); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
展示(Dumb Components)组件和容器(Smart Components)组件
还记得我们的第二个问题吗?组件由于 context 的侵入而变得不能复用。React-Redux 为领会决这个问题,将所有组件分成两大类:展示组件和容器组件。
展示组件
展示组件有几个特征
1. 组件只卖力 UI 的展示,没有任何营业逻辑
2. 组件没有状态,即不使用 this.state
3. 组件的数据只由 props 决议
4. 组件不使用任何 Redux 的 API
展示组件就和纯函数一样,返回效果只依赖于它的参数,并且在执行历程内里没有副作用,让人以为异常的靠谱,可以放心的使用。
作甚内存重排序?
import React, { Component } from 'react'; import PropTypes from 'prop-types'; class Title extends Component { static propTypes = { title: PropTypes.string } render () { return ( <h1>{ this.props.title }</h1> ) } }
像这个 Title 组件就是一个展示组件,组件的效果完全由外部传入的 title 属性决议。
容器组件
容器组件的特征则相反
1. 组件卖力治理数据和营业逻辑,不卖力 UI 展示
2. 组件带有内部状态
3. 组件的数据从 Redux state 获取
4. 使用 Redux 的 API
你可以直接使用 store.subscribe()
来手写容器组件,然则不建议这么做,由于这样无法使用 React-redux 带来的性能优化。
React-redux 划定,所有的展示组件都由用户提供,容器组件则是由 React-Redux 的 connect()
自动天生。
高阶组件 Connect
React-redux 提供 connect
方式,可以将我们界说的展示组件天生容器组件。connect 函数接受一个展示组件参数,最后会返回另一个容器组件回来。以是 connect 其实是一个高阶组件(高阶组件就是一个函数,传给它一个组件,它返回一个新的组件)。
import { connect } from 'react-redux'; import Header from '../components/Header'; export default connect()(Header);
上面代码中,Header 就是一个展示组件,经由 connect 处置后变成了容器组件,最后把它导出成模块。这个容器组件没有界说任何的营业逻辑,所有不能做任何事情。我们可以通过 mapStateToProps
和 mapDispatchToProps 来界说我们的营业逻辑。
import { connect } from 'react-redux'; import Title from '../components/Title'; const mapStateToProps = (state) => { return { title: state.title } } const mapDispatchToProps = (dispatch) => { return { onChangeColor: (color) => { dispatch({ type: 'CHANGE_COLOR', color }); } } } export default connect(mapStateToProps, mapDispatchToProps)(Title);
mapStateToProps 告诉 connect 我们要取 state 里的 title 数据,最终 title 数据会以 props 的方式传入 Title 这个展示组件。
mapStateToProps 还
会订阅 Store,每当 state 更新的时刻,就会自动执行,重新盘算展示组件的参数,从而触发展示组件的重新渲染。
mapDispatchToProps 告诉 connect 我们需要 dispatch action,最终 onChangeColor 会以 props 回调函数的方式传入 Title 这个展示组件。
Connect 组件也许的实现如下
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => { class Connect extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { allProps: {} } } componentWillMount () { const { store } = this.context this._updateProps() store.subscribe(() => this._updateProps()) } _updateProps () { const { store } = this.context let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) // 将 Store 的 state 和容器组件的 state 传入 mapStateToProps : {} // 判断 mapStateToProps 是否传入 let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) // 将 dispatch 方式和容器组件的 state 传入 mapDispatchToProps : {} // 判断 mapDispatchToProps 是否传入 this.setState({ allProps: { ...stateProps, ...dispatchProps, ...this.props } }) } render () { // 将 state.allProps 睁开以容器组件的 props 传入 return <WrappedComponent {...this.state.allProps} /> } } return Connect }
小结
至此,我们就很清晰了,原来 React-redux 就是通过 Context 连系 Redux 来实现 React 应用的状态治理,通过 Connect 这个高阶组件来实现展示组件和容器组件的毗邻的。
原创文章,作者:28rg新闻网,如若转载,请注明出处:https://www.28rg.com/archives/1102.html