2

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

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

要将方法传递给子组件或上下文提供程序值,您似乎总是不得不执行以下操作(请注意,在本示例中,我根据反应文档)。

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

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

编辑

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

4

2 回答 2

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给孩子,但不建议这样做,因为这会破坏封装并与最小特权原则相矛盾。

于 2018-10-04T19:02:31.953 回答
0

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

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

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

this.Container.coolMethod()

编辑

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

于 2018-10-04T17:22:05.277 回答