【问题标题】:React Context: passing all of a component's methods at onceReact Context:一次传递一个组件的所有方法
【发布时间】:2019-03-10 03:13:49
【问题描述】:

假设我有一个用于处理应用程序逻辑的容器组件,它有很多方法:

class ScreenContainer extends React.Component
{
    state = {
        inputs: { /* various properties for input values */ },
        thingyActive: false,
        someList: ["thing1", "thing2"],
        // ...etc.
    };

    handleInputChange = e => {
        const { name, value } = e.target;
        this.setState(prevState => ({
            inputs: { ...prevState.inputs, [name]: value }
        }));
    };

    toggleThingy = () => this.setState(prevState => ({
        thingyActive: !prevState.thingyActive
    }));

    coolMethod = () => { /* Do cool stuff */ };

    boringMethod = () => { /* Do boring stuff */ };

    // ...more methods...
}

我需要所有这些方法都可以被内部组件访问。我将在本例中使用上下文提供程序,我们只会说上下文被构成应用程序屏幕的各种嵌套表示组件使用。

const ScreenContext = React.createContext();

要将方法传递给子组件或上下文提供程序值,您似乎总是不得不执行以下操作(请注意,在本示例中,我根据建议将“操作”提升为状态在React documentation 中给出)。

class ScreenContainer extends React.Component
{
    constructor()
    {
        super();
        this.state = {
            // ...same state as before, plus:
            actions: {
                handleInputChange: this.handleInputChange,
                toggleThingy: this.toggleThingy,
                coolMethod: this.coolMethod,
                boringMethod: this.boringMethod,
                everySingleOtherMethod: this.everySingleOtherMethod,
                // ...on and on
            }
        };
    }

    // ...same methods as before...

    render()
    {
        return (
            <ScreenContext.Provider value={this.state}>
                {this.props.children}
            </ScreenContext.Provider>
        );
    }

我一直在寻找一种方法来避免一个一个地通过它们。我发现的一个可能的解决方案是使用 getter 并循环遍历类实例属性,如下所示:

    get allMethods()
    {
        let output = {};

        for (var prop in this)
        {
            if (this.hasOwnProperty(prop) && typeof this[prop] === "function")
                output[prop] = this[prop];
        }

        return output;
    }

那我就可以了:

    // (in ScreenContainer constructor)

    this.state = {
        // ...state,
        actions: this.allMethods
    };

如果需要,也可以将 getter 代码提取到实用程序函数中,以便在其他容器类型的组件中重用。显然,只有当有大量的方法需要传递时,这才是值得的。

它看起来很简单,只要在构造函数中完成就可以正常工作。这有什么疯狂的吗?以任何方式这是不好的做法,还是有任何我不知道的潜在副作用?有没有更好的方法我错过了?

编辑

我已更新示例以更接近我的真实代码;它现在显示了方法可能做的事情,并使用上下文设置,而不是将方法作为道具传递给单个子组件。

【问题讨论】:

  • 我希望您知道,如果您需要将大量方法传递给单个子组件,您的架构很可能存在问题
  • 正确的设计应该是“分离关注点”。如果只有子级使用它,则不要在父级中定义函数。最好在子组件中定义它。并且请不要遍历组件来查找功能。我的建议是第一个。而且如果嵌套的children很多,那就试试react redux吧。
  • 我在另一个问题上对这个问题有类似的回答,我想我会分享:“在 React 中的组件之间共享功能的正确方法”stackoverflow.com/a/51661103/2430549
  • @DanielHilgarth 我认为你是对的,但我做了一些修改,希望能更好地阐明我的情况。

标签: reactjs class methods getter react-context


【解决方案1】:

如果一个类不维护状态,并且类方法应该单独用作辅助函数,那么它们不应该是类的一部分,更不用说类组件了。在这种情况下,一个类充当命名空间。在现代 JavaScript 中,模块被用作命名空间。可以是:

export const coolMethod = () => { /* Do cool stuff */ };

export const coolerMethod = () => { /* Do even cooler stuff */ };

export const boringMethod = () => { /* Do boring but necessary stuff */ };

ScreenContainer 组件是“智能”容器组件的示例。总是最好明确列出传递的函数,而不是自动传递它们。 ScreenContainer 可能会在某个时候获得私有方法。并且应该保证生命周期钩子也不会被意外传递。

如果它应该有一个孩子,它可以作为高阶组件应用:

const withScreen(Comp) => {
  return class ScreenContainer extends React.Component {
    ...
    render() {
      return <Comp handleInputChange={this.handleInputChange} /* ... */ />;
    }
  }
}

在这种特殊情况下,render 可以与传递函数区分开来,因为后者是实例方法(箭头函数)。虽然通常不推荐使用这种魔法,因为它可能会导致问题并且无法正常用于私有方法,但可以将其缩短为:

    render() {
      const fns = {};

      for (const method of Object.keys(this)) {
        if (typeof this[method] === 'function')
          fns[method] = this[method];
      }

      return <Comp {...fns} {...this.props} />;
    }

对于多个孩子,ScreenContainer children 可以通过类似的方式遍历添加道具。

对于间接子级,可以使用上下文 API 来传递函数。

虽然可以将ScreenContainer this 传递给孩子,但不建议这样做,因为这会破坏封装并与the principle of least privilege 相矛盾。

【讨论】:

  • 这是一个很好的观点,但我应该澄清一下这些方法正在做的“很酷的事情”可能包括操纵状态。我认为另一个答案下的 cmets 是正确的 - 我的问题可能是架构问题,表明我应该使用 Redux 或类似的东西。
  • 考虑用您的实际代码(或其简化版本)更新问题,解决方案可能取决于它。它可以用 HOC 或其他方式解决。 Redux 确实是一种可靠的方式,但不是唯一的方式。
  • 我已将问题编辑为更接近我的实际代码。
  • 我试图解决所有问题。我仍然不确定这个容器的用例是什么。但是你需要为所有嵌套组件提供函数,那么是的,这应该是 Redux 或 React 上下文。
【解决方案2】:

我这样做的一种方法是在子组件的构造函数中实例化一个新实例,如下所示:

class ChildComponent extends Component {
  constructor(props) {
  super(props);
  this.Container = new MyContainer();
}

然后您可以使用任何方法,例如:

this.Container.coolMethod()

编辑

我误会了。我只是通过创建一个您实例化的助手类而不是组件来完成此操作。当您有想要在多个组件中使用的方法而不必通过组件树将所有方法作为 props 传递时,它会很有帮助。

【讨论】:

  • 你不应该手动实例化 React 组件,除非是为了测试目的。
猜你喜欢
  • 1970-01-01
  • 2019-10-23
  • 2022-08-17
  • 2022-10-18
  • 1970-01-01
  • 2019-04-20
  • 2021-10-15
  • 1970-01-01
  • 2021-08-13
相关资源
最近更新 更多