React Context:一次传递所有组件的方法

React Context: passing all of a component's methods at once

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

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();

要将方法向下传递给子组件或传递给上下文提供程序值,您似乎总是不得不执行如下所示的操作(请注意,我将 "actions" 提升到状态根据 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 并循环遍历 class 实例属性,如下所示:

    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 代码也可以提取到实用函数中,以便在需要时在其他容器类型组件中重用。显然,只有在有大量方法可以传承的情况下,这才值得。

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

编辑

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

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

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

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

this.Container.coolMethod()

编辑

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

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

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

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

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

ScreenContainer 组件是 'smart' 容器组件的示例。明确列出传递的函数总是比自动传递它们更好。 ScreenContainer 可能会在某些时候获得私有方法。并且应该保证生命周期挂钩也不会被意外传递。

如果它应该有一个单一的child,它可以作为higher-order组件应用:

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} />;
    }

对于多个children,可以遍历ScreenContainerchildren以类似的方式添加道具。

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

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