假设我有一个用于处理应用程序逻辑的容器组件,该组件有很多方法:
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...
}
我需要所有这些方法以使内部组件可以访问。在此示例中,我将使用Context提供程序,而我们只是说上下文由构成应用程序屏幕的各种嵌套表示组件所使用。
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>
);
}
我一直在寻找一种避免将它们一一传递的方法。我发现一个可能的解决方案涉及使用吸气剂并遍历类实例属性,如下所示:
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
};
如果需要,还可以将吸气剂代码提取到实用程序函数中,以便在其他容器类型的组件中重用。显然,这只有在要传递大量方法的情况下才值得。
看起来很简单,只要在构造器中完成就可以正常工作。这有什么疯狂的吗?这是不好的做法吗,还是有我不知道的潜在副作用?是否可能有更好的方式让我丢失?
我已经更新了示例,使其更接近我的真实代码;现在,它显示了方法可以执行的操作以及使用Context设置,而不是将方法作为道具传递给单个子组件。
答案 0 :(得分: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相矛盾。
答案 1 :(得分:0)
我这样做的一种方法是在子组件的构造函数中实例化一个新实例,如下所示:
class ChildComponent extends Component {
constructor(props) {
super(props);
this.Container = new MyContainer();
}
然后您可以使用任何方法,例如:
this.Container.coolMethod()
修改
我误会了。我只是通过创建实例化的帮助程序类(而不是组件)来完成此操作。当您具有要在多个组件中使用的方法而不必将所有方法作为道具通过组件树时,这将很有帮助。