使用react-redux的connect()函数

我们今天翻译的文章将讨论如何在React应用程序中创建与Redux状态相关的容器组件。 该材料基于使用react-redux包的 React中状态管理机制的描述。 假定您已经对我们将要讨论的库的体系结构和API有了基本的了解。 如果不是这种情况,请参阅ReactRedux文档。

图片

关于JavaScript应用程序中的状态管理


React为开发人员提供了两种将数据传输到组件的主要机制。 这些是属性(属性)和状态。 属性是只读的,并允许父组件将属性传递给子组件。 状态是封装在组件中的本地实体,可以在组件的生命周期中随时更改。

由于状态是一种用于创建强大的动态React应用程序的极其有用的机制,因此有必要对其进行适当的管理。 当前有几个库提供了用于管理应用程序运行状况的结构良好的体系结构。 其中包括FluxReduxMobX

Redux是一个旨在创建用于存储应用程序状态的容器的库。 它为开发人员提供了行为可预测的易于理解的状态管理工具。 该库适用于用纯JavaScript编写的应用程序,以及使用某些框架进行开发的项目。 Redux的体积很小,但是它允许您编写可在不同环境中运行的可靠应用程序。

以下是创建Redux存储库的方法:

import { createStore } from 'redux'; const initialState = {    auth: { loggedIn: false } } const store = createStore((state = initialState, action) => {    switch (action.type) {        case "LOG_IN":            return { ...state, auth: { loggedIn: true } };            break;        case "LOG_OUT":            return { ...state, auth: { loggedIn: false } };            break;        default:            return state;            break;    }    }) 

React-redux包


react-redux软件包为Redux状态容器提供了React绑定,这使得将React应用程序连接到Redux仓库非常容易。 这使您可以根据React应用程序与存储库的关系来分离它们。 即,我们正在谈论以下类型的组件:

  1. 演示组件。 他们仅负责应用程序的外观,并不了解Redux的状态。 它们通过属性接收数据并可以调用回调,回调也通过属性传递给它们。
  2. 容器组件。 它们负责应用程序内部机制的操作,并与Redux状态交互。 它们通常是使用react-redux创建的,可以调度Redux操作。 另外,他们订阅状态更改。

可以在此处找到有关此方法的详细信息,以明确各个组件的职责。 在本文中,我们将主要讨论使用react-redux连接到Redux状态的容器组件。

react-redux软件包的界面非常简单。 特别是,有关此接口的最有趣的事情归结为以下几点:

  1. <Provider store> -允许您为React应用程序创建包装,并使Redux状态可用于其层次结构中的所有容器组件。
  2. connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) -允许您创建高阶组件。 这是基于基本React组件创建容器组件所必需的。

安装react-redux以便在项目中使用此软件包,如下所示:

 npm install react-redux --save 

基于您已经为React应用程序配置了Redux存储库的假设,以下是将应用程序连接到Redux存储库的示例:

 import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import createStore from './createReduxStore'; const store = createStore(); const rootElement = document.getElementById('root'); ReactDOM.render(( <Provider store={store}>   <AppRootComponent /> </Provider> ), rootElement); 

现在,您可以创建连接到Redux存储库的容器组件。 这是使用connect() API在AppRootComponent的层次结构中完成的。

什么时候使用connect()?


container创建容器组件


如前所述,react-redux connect() API用于创建连接到Redux存储库的容器组件。 使用React上下文机制从组件的最高祖先获得您要连接的存储。 如果仅创建演示文稿组件,则不需要connect()函数。

如果您在React组件中需要从存储中接收数据,或者需要分派操作,或者您需要同时执行这两项操作,则可以将常规组件包装到由connect()返回的高阶组件中,从而将其转换为容器组件react-redux。 看起来是这样的:

 import React from 'react'; import { connect } from 'react-redux'; import Profile from './components/Profile'; function ProfileContainer(props) { return (   props.loggedIn     ? <Profile profile={props.profile} />     : <div>Please login to view profile.</div> ) } const mapStateToProps = function(state) { return {   profile: state.user.profile,   loggedIn: state.auth.loggedIn } } export default connect(mapStateToProps)(ProfileContainer); 

▍无需手动订阅Redux存储


您可以自己创建容器组件,并使用store.subscribe()命令在Redux存储库上手动签名该组件。 但是,使用connect()函数意味着应用了一些性能改进和优化,这些性能和优化可能在使用其他机制时无法使用。

在以下示例中,我们尝试手动创建容器组件,并通过订阅将其连接到Redux存储库。 在这里,我们努力实现与上例相同的功能。

 import React, { Component } from 'react'; import store from './reduxStore'; import Profile from './components/Profile'; class ProfileContainer extends Component { state = this.getCurrentStateFromStore() getCurrentStateFromStore() {   return {     profile: store.getState().user.profile,     loggedIn: store.getState().auth.loggedIn   } } updateStateFromStore = () => {   const currentState = this.getCurrentStateFromStore();     if (this.state !== currentState) {     this.setState(currentState);   } } componentDidMount() {   this.unsubscribeStore = store.subscribe(this.updateStateFromStore); } componentWillUnmount() {   this.unsubscribeStore(); } render() {   const { loggedIn, profile } = this.state;     return (     loggedIn       ? <Profile profile={profile} />       : <div>Please login to view profile.</div>   ) } } export default ProfileContainer; 

此外, connect()函数为开发人员提供了更大的灵活性,使您可以配置容器组件以基于最初传递给它们的属性来接收动态属性。 事实证明,这对于从基于属性的状态中获取选择或将操作生成器与属性中的特定变量链接在一起非常有用。

如果您的React应用程序使用多个Redux存储库,那么connect()可以轻松指定容器组件应连接到的特定存储库。

解剖连接()


react-redux软件包提供的connect()函数最多可以包含四个参数,每个参数都是可选的。 调用connect()函数后,将返回一个高阶组件,该组件可用于包装任何React组件。

由于该函数返回一个高阶组件,因此需要再次调用它,并传递基本的React组件以将其转换为容器组件:

 const ContainerComponent = connect()(BaseComponent); 

这是connect()函数的签名:

 connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) 

▍mapStateToProps参数


mapStateToProps参数是一个返回常规对象或其他函数的函数。 传递此connect()参数connect()容器组件预订到Redux存储库更新。 这意味着每次存储库状态更改时,都会调用mapStateToProps函数。 如果您不想监视状态更新, connect()作为此参数的值传递给undefinednull

使用两个参数声明mapStateToProps函数,其中第二个参数是可选的。 第一个参数是Redux存储库的当前状态。 第二个参数(如果传递)是传递给组件的属性的对象:

 const mapStateToProps = function(state) { return {   profile: state.user.profile,   loggedIn: state.auth.loggedIn } } export default connect(mapStateToProps)(ProfileComponent); 

如果从mapStateToProps返回了常规对象,则返回的stateProps对象将与组件的属性组合。 您可以按以下方式在组件中访问这些属性:

 function ProfileComponent(props) { return (   props.loggedIn     ? <Profile profile={props.profile} />     : <div>Please login to view profile.</div> ) } 

如果mapStateToProps返回一个函数,则此函数用作mapStateToProps的每个实例的mapStateToProps 。 这对于提高渲染性能和进行备忘录很有用。

▍mapDispatchToProps参数


mapDispatchToProps参数可以是对象或返回常规对象或另一个函数的函数。 为了更好地说明mapDispatchToProps ,我们需要动作生成器。 假设我们有以下生成器:

 export const writeComment = (comment) => ({ comment, type: 'WRITE_COMMENT' }); export const updateComment = (id, comment) => ({ id, comment, type: 'UPDATE_COMMENT' }); export const deleteComment = (id) => ({ id, type: 'DELETE_COMMENT' }); 

现在考虑mapDispatchToProps的各种用法。

默认默认实现


如果不使用由对象或函数表示的自己的mapDispatchToProps实现,则将使用标准实现,该实现将dispatch()存储库方法作为组件的属性来实现。 您可以在以下组件中使用此属性:

 import React from 'react'; import { connect } from 'react-redux'; import { updateComment, deleteComment } from './actions'; function Comment(props) { const { id, content } = props.comment; //    props.dispatch() const editComment = () => props.dispatch(updateComment(id, content)); const removeComment = () => props.dispatch(deleteComment(id)); return (   <div>     <p>{ content }</p>     <button type="button" onClick={editComment}>Edit Comment</button>     <button type="button" onClick={removeComment}>Remove Comment</button>   </div> ) } export default connect()(Comment); 

对象转移


如果将对象用作mapDispatchToProps的参数,则该对象中的每个函数将被视为Redux动作生成器,并包装在dispatch()存储库方法调用中,这将允许直接调用它。 具有动作生成器dispatchProps的结果对象将与组件的属性组合。

以下示例显示了构造mapDispatchToProps参数的示例,该参数是具有动作生成器的对象,以及如何将生成器用作React组件的属性:

 import React from 'react'; import { connect } from 'react-redux'; import { updateComment, deleteComment } from './actions'; function Comment(props) { const { id, content } = props.comment; // ,   ,   const editComment = () => props.updatePostComment(id, content); const removeComment = () => props.deletePostComment(id); return (   <div>     <p>{ content }</p>     <button type="button" onClick={editComment}>Edit Comment</button>     <button type="button" onClick={removeComment}>Remove Comment</button>   </div> ) } //     const mapDispatchToProps = { updatePostComment: updateComment, deletePostComment: deleteComment } export default connect(null, mapDispatchToProps)(Comment); 

功能转移


当使用mapDispatchToProps函数作为参数时mapDispatchToProps程序员必须注意返回dispatchProps对象,该对象使用dispatch()存储方法绑定动作生成器。 此函数接受dispatch()存储库方法作为第一个参数。 与mapStateToProps ,该函数还可以接受可选的第二个参数ownProps ,该参数描述具有传递给组件的原始属性的映射。

如果此函数返回另一个函数,则返回的函数将用作mapDispatchToProps ,这对于改善渲染性能和mapDispatchToProps很有用。

Redux中的bindActionCreators()帮助函数可在该函数内部使用,以将动作生成器绑定到dispatch()存储库方法。

下面的示例以mapDispatchToProps的角色显示函数的mapDispatchToProps 。 它还演示了辅助功能bindActionCreators()的工作,该功能用于绑定动作生成器以与对React组件的props.actions的注释props.actions使用:

 import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import * as commentActions from './actions'; function Comment(props) { const { id, content } = props.comment; const { updateComment, deleteComment } = props.actions; //    props.actions const editComment = () => updateComment(id, content); const removeComment = () => deleteComment(id); return (   <div>     <p>{ content }</p>     <button type="button" onClick={editComment}>Edit Comment</button>     <button type="button" onClick={removeComment}>Remove Comment</button>   </div> ) } const mapDispatchToProps = (dispatch) => { return {   actions: bindActionCreators(commentActions, dispatch) } } export default connect(null, mapDispatchToProps)(Comment); 

merge参数mergeProps


如果将mergeProps参数传递给connect() ,则它是一个具有以下三个参数的函数:

  • stateProps是从mapStateToProps()调用返回的属性对象。
  • dispatchProps具有mapDispatchToProps()动作生成器的属性对象。
  • ownProps组件获得的原始属性。

此函数返回一个简单的对象,该对象的属性将传递给包装的组件。 这对于有条件地映射Redux存储库或基于属性的操作生成器的部分状态很有用。

如果connect()没有通过此函数,则使用其标准实现:

 const mergeProps = (stateProps, dispatchProps, ownProps) => { return Object.assign({}, ownProps, stateProps, dispatchProps) } 

representing用参数表示对象的参数


作为第四个参数传递给connect()函数的可选对象,包含旨在更改此函数的行为的参数。 因此, connect()connectAdvanced()函数的特殊实现,它接受connectAdvanced()可用的大多数参数以及一些其他参数。

阅读完之后, 这是文档页面,您可以找到可以与connect()一起使用的参数,以及它们如何修改此函数的行为。

使用connect()函数


▍创建存储


在使用connect()将常规React组件转换为容器组件之前,您需要创建一个Redux存储库,该组件将连接到该存储库。

假设我们有一个容器组件NewComment ,该组件用于向发布中添加新评论,并另外显示一个用于发送评论的按钮。 描述此组件的代码可能如下所示:

 import React from 'react'; import { connect } from 'react-redux'; class NewComment extends React.Component { input = null writeComment = evt => {   evt.preventDefault();   const comment = this.input.value;     comment && this.props.dispatch({ type: 'WRITE_COMMENT', comment }); } render() {   const { id, content } = this.props.comment;     return (     <div>       <input type="text" ref={e => this.input = e} placeholder="Write a comment" />       <button type="button" onClick={this.writeComment}>Submit Comment</button>     </div>   ) } } export default connect()(NewComment); 

为了在应用程序中使用此组件,有必要描述该组件必须连接到的Redux存储库。 否则,将发生错误。 这可以通过两种方式来完成,我们现在将考虑。

在容器组件中设置商店属性


为组件配备Redux存储库的第一种方法是将指向该存储库的链接传递为组件的store属性值:

 import React from 'react'; import store from './reduxStore'; import NewComment from './components/NewComment'; function CommentsApp(props) { return <NewComment store={store} /> } 

在<Provider>组件中设置store属性


如果您只想为应用程序设置Redux存储库一次,那么您将对我们现在考虑的方法感兴趣。 它通常适用于仅使用一个Redux存储库的应用程序。

react-redux软件包为开发人员提供了<Provider>组件,该组件可用于包装应用程序的根组件。 它接受store属性。 假定它是到计划在应用程序中使用的Redux存储库的链接。 根据应用程序的层次结构,使用React上下文机制将store属性传递给容器组件:

 import React from 'react'; import ReactDOM from 'react-dom'; import store from './reduxStore'; import { Provider } from 'react-redux'; import NewComment from './components/NewComment'; function CommentsApp(props) { return <NewComment /> } ReactDOM.render(( <Provider store={store}>   <CommentsApp /> </Provider> ), document.getElementById('root')) 

ProOpenProps访问组织


如前所述,可以使用第二个参数ownProps声明mapStateToPropsconnect()mapStateToPropsmapDispatchToProps函数,该参数是组件的属性。
但是,有一个问题。 如果已声明函数的必需参数数量小于2,则将不会传输ownProps 。 但是,如果声明的函数没有必需的参数或带有至少2个参数,则将传递ownProps

考虑使用ownProps几种选择。

没有参数的函数声明


 const mapStateToProps = function() { console.log(arguments[0]); // state console.log(arguments[1]); // ownProps }; 

在这种情况下,由于声明了函数而没有必需的参数, ownProps传递ownProps 。 因此,下面的代码将为其余的ES6参数使用新语法编写:

 const mapStateToProps = function(...args) { console.log(args[0]); // state console.log(args[1]); // ownProps }; 

一个参数的函数声明


考虑以下示例:

 const mapStateToProps = function(state) { console.log(state); // state console.log(arguments[1]); // undefined }; 

只有一个参数state 。 结果,由于未传输ownProps ,因此arguments[1]的值undefined

具有默认参数的函数声明


 const mapStateToProps = function(state, ownProps = {}) { console.log(state); // state console.log(ownProps); // {} }; 

只有一个必需的参数state ,因为第二个参数ownProps是可选的,因为它具有默认值。 结果,由于只有一个必需的参数, ownProps不会传递ownProps ,并且将使用为其分配的默认值(即,使用空对象)执行映射。

声明带有两个参数的函数


 const mapStateToProps = function(state, ownProps) { console.log(state); // state console.log(ownProps); // ownProps }; 

一切都安排得非常简单。 即,在这种情况下,由于使用两个必需的参数声明了函数,因此ownPropsownProps的传输。

总结


掌握了这些材料之后,您了解了何时以及如何使用react-redux软件包提供的connect() API,该API旨在创建连接到Redux状态的容器组件。 这里我们详细讨论了connect()函数的结构以及如何使用它,但是,如果您想了解更多有关此机制的信息,特别是要熟悉其使用案例,请查看一下react-redux文档的这一部分。

亲爱的读者们! 您是否在项目中使用react-redux?

Source: https://habr.com/ru/post/zh-CN423157/


All Articles