react-redux connect()-ed容器可以实现像componentDidMount这样的生命周期方法吗?

sto*_*one 30 reactjs react-redux

我在react-redux网站上遇到了一个重复模式:一个组件显示来自web api的数据,它应该在加载时自动填充,无需任何用户交互.

我想从容器组件启动异步提取,但据我所知,唯一的方法是从显示组件中的生命周期事件.这似乎使得无法将所有逻辑放在容器中,只使用哑无状态功能组件进行显示.

这意味着我不能将无状态功能组件用于任何需要异步数据的组件.这似乎不对.

似乎"正确"的方法是以某种方式从容器启动异步调用.然后,当调用返回时,状态将被更新,容器将获得新状态,然后将其传递给其无状态组件mapStateToProps().

进行异步调用mapStateToProps和mapDispatchToProps(我的意思是实际调用异步函数,而不是将其作为属性返回)没有意义.

所以我最终做的是将异步调用放在一个refreshData()公开的函数中mapDispatchToProps(),然后从两个或多个React生命周期方法中调用它:componentDidMount and componentWillReceiveProps.

有没有一种干净的方法来更新redux存储状态而不在每个需要异步数据的组件中放入生命周期方法调用?

我是否应该将这些调用放在组件层次结构的更高位置(从而减少此问题的范围,因为只有"顶级"组件才需要监听生命周期事件)?

编辑:

就这样,我对connect()ed容器组件的意思没有任何混淆,这是一个非常简单的例子:

import React from 'react';
import { connect } from 'react-redux';
import {action} from './actions.js';

import MyDumbComponent from './myDumbComponent.jsx';

function mapStateToProps(state)
{
  return { something: state.xxxreducer.something  };
}

function mapDispatchToProps(dispatch)
{
  return { 
       doAction: ()=>{dispatch(action())}
  };
}

const MyDumbComponentContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(MyDumbComponent);

// Uh... how can I hook into to componentDidMount()? This isn't 
// a normal React class.

export default MyDumbComponentContainer;
Run Code Online (Sandbox Code Playgroud)

sto*_*one 8

Jamie Dixon写了一个包来做这个!

https://github.com/JamieDixon/react-lifecycle-component

用法如下所示:

const mapDispatchToProps = {
    componentDidMount: getAllTehDatas
}

...

export default connectWithLifecycle(mapStateToProps, mapDispatchToProps)(WrappedComponent) 
Run Code Online (Sandbox Code Playgroud)


jin*_*ula 7

编辑 使用钩子,您现在可以在无状态功能组件中实现生命周期回调.虽然这可能无法直接解决问题中的所有问题,但它也可能解决了一些想要做最初提议的事情的原因.


编辑到原始答案 在评论讨论和思考之后,这个答案更具探索性,可以作为对话的一部分.但我不认为这是正确的答案.

原始答案

在Redux站点上有一个示例,显示您不必同时执行mapStateToProps和mapDispatchToProps.你可以利用connect道具的强大功能,使用一个类并在哑组件上实现生命周期方法.

在该示例中,连接调用甚至在同一文件中,甚至不导出哑组件,因此对于组件的用户来说它看起来是相同的.

我可以理解不想从显示组件发出异步调用.我认为从那里发出异步调用和调度一个动作之间存在区别,该动作通过thunk将异步调用的发布移动到动作中(甚至更多地与React代码分离).

作为一个例子,这里是一个启动画面组件,我想在显示组件安装时执行一些异步操作(如资源预加载):

SplashContainer.js

import { connect } from 'react-redux'
import Splash from '../components/Splash'
import * as actions from '../actions'

const mapStateToProps = (state) => {
  return {
    // whatever you need here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onMount: () => dispatch(actions.splashMount())
  }
}

const SceneSplash = connect(
  mapStateToProps,
  mapDispatchToProps
)(Splash)

export default SceneSplash
Run Code Online (Sandbox Code Playgroud)

Splash.js

import React from 'react'

class Splash extends React.Component {
  render() {
    return (
      <div className="scene splash">
      <span className="fa fa-gear fa-spin"></span>
      </div>
    )
  }

  componentDidMount() {
    const { onMount } = this.props
    onMount()
  }
}

export default Splash
Run Code Online (Sandbox Code Playgroud)

您可以看到调度发生在连接的容器中,您可以想象在actions.splashMount()调用中我们发出异步http请求或通过thunks或promises 执行其他异步操作.

编辑澄清

请允许我尝试捍卫这种方法.我重新阅读了这个问题,并不是100%肯定我正在解决它之后的主要问题,但请耐心等待.如果我还没有完全按计划进行,我可以在下面修改一个可能更接近标记的方法.

"它应该在负载上填充" - 上面的例子实现了这一点

"我想从容器启动异步提取" - 在示例中,它不是从显示组件或容器启动的,而是从异步操作启动的

"这似乎使得无法将所有逻辑放入容器中" - 我认为您仍然可以在容器中添加任何其他逻辑.如上所述,数据加载代码不在显示组件(或容器)中,而是在异步操作创建器中.

"这意味着我不能为任何需要异步数据的组件使用无状态功能组件." - 在上面的例子中,显示组件是无状态和功能的.唯一的链接是调用回调的生命周期方法.它不需要知道或关心回调的作用.这不是显示组件试图成为异步数据获取的所有者的情况 - 它只是让处理它的代码知道特定事件何时发生.

到目前为止,我试图证明给出的例子如何满足问题的要求.也就是说,如果您所拥有的是显示组件,即使是间接回调,也绝对不包含与异步数据加载相关的代码 - 也就是说,它唯一的链接就是通过它所传递的道具来消耗该数据.远程数据下来,然后我会建议这样的事情:

SplashContainer.js

import { connect } from 'react-redux'
import Splash from '../components/Splash'
import * as actions from '../actions'

const mapStateToProps = (state) => {
  return {
    // whatever you need here
  }
}

const mapDispatchToProps = (dispatch) => {
  dispatch(actions.splashMount())
  return {
    // whatever else here may be needed
  }
}

const SceneSplash = connect(
  mapStateToProps,
  mapDispatchToProps
)(Splash)

export default SceneSplash
Run Code Online (Sandbox Code Playgroud)

Splash.js

import React from 'react'

class Splash extends React.Component {
  // incorporate any this.props references here as desired
  render() {
    return (
      <div className="scene splash">
      <span className="fa fa-gear fa-spin"></span>
      </div>
    )
  }
}

export default Splash
Run Code Online (Sandbox Code Playgroud)

通过在mapDispatchToProps中调度操作,您可以让该操作的代码完全驻留在容器中.实际上,只要实例化容器,就会启动异步调用,而不是等待连接的显示组件旋转并挂载.但是,如果在显示组件的componentDidMount()触发之前无法启动异步调用,我认为您本身就必须拥有像我的第一个示例中那样的代码.

我实际上没有测试过第二种方法,看看react或redux是否会抱怨它,但它应该有效.您可以访问调度方法,并且应该可以毫无问题地调用它.

老实说,第二个例子,虽然从显示组件中删除了与async动作相关的所有代码,但有点让我感到有点滑稽,因为我们正在以同名方式执行非映射派对的事情.功能.并且容器实际上没有componentDidMount来运行它.所以我对它有点畏缩,并倾向于第一种方法.它在"感觉正确"的意义上并不干净,但它处于"简单的单线"感.