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,可以遍历ScreenContainer
children
以类似的方式添加道具。
对于间接children,上下文API可以用来传递函数。
虽然可以将 ScreenContainer
this
传递给 children,但不推荐这样做,因为这会破坏封装并与 the principle of least privilege 相矛盾。
假设我有一个用于处理应用程序逻辑的容器组件,它有很多方法:
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,可以遍历ScreenContainer
children
以类似的方式添加道具。
对于间接children,上下文API可以用来传递函数。
虽然可以将 ScreenContainer
this
传递给 children,但不推荐这样做,因为这会破坏封装并与 the principle of least privilege 相矛盾。