我可能做错了此事,或者以一种不好的方式使用了上下文。我是新来的反应者,所以我不知道这是否意味着我们要做事。
我的理解:
上下文可用于将props传递给更深层的嵌套子组件,而不必将它们传递给所有嵌套级别。提供者充满了道具,而消费者将在“树上查找”以找到最近的提供者并获取其数据。
如果是这种情况,那么我可以为提供程序加载函数,例如onChange处理程序,以避免在每个子组件都执行相同操作时必须在每个子组件上编写处理程序。这将允许一个“智能表单”,通过“传递”给它的处理程序来管理其输入的处理程序。显然,在多个组件上编写一个处理程序不是问题,但是拥有20-30个表单域并在每个组件上编写4+处理程序只会造成代码混乱。所以我尝试了以下方法:
HTML结构如下:
<ControlledForm data={some_data} handlers={some_handlers}>
<LabeledControl name="Type your name" rel="Name" meta={{some_meta_object}}></LabeledControl>
<LabeledControl name="Pet name" rel="PetName" meta={{some_meta_object}}></LabeledControl>
<LabeledControl name="Type of pet" rel="PetType" meta={{some_meta_object}}></LabeledControl>
<LabeledControl name="Family" rel="Family" meta={{some_meta_object}}></LabeledControl>
</ControlledForm>
这是ControlledForm
类代码:
const { Provider } = React.createContext(); //Note this
class ControlledForm extends Component {
state = {};
render() {
return (
<Provider value={{ onChange: this.props.onChange }}>
<form>{this.props.children}</form>
</Provider>
);
}
}
现在,无论我放置在此表单中的哪个孩子,都希望在其周围有一个<Consumer>
包装器以消耗changeHandler
,或者至少这是计划。但是,当我将LabeledControl
包装到使用者中时,它的作用就好像没有数据一样。
<LabeledControl>
(简化代码):
const { Consumer } = React.createContext();
class LabeledControl extends Component {
state = {};
render() {
return (
<Consumer>
{r => {
console.log("consumer:", r); //Logs undefined
return (
<div className="labeled-control">
{/*Code here*/}
</div>
);
}}
</Consumer>
);
}
}
如果我要猜测问题出在哪里,那是因为ControlledForm
和LabeledControl
都创建了自己的上下文(不共享),请看上面的代码。但是我不明白如何共享此上下文,并且仍然将这两个类保存在单独的.js文件中。我无法将参考传递给孩子们,我得到的只是{this.props.children}
,而且没有办法告诉它“嘿,在这里使用这个提供者”。我在网上找到的所有示例在同一文件中都有提供者和使用者这两个类,它们可以引用相同的“上下文”,但这严重影响了我可以在表单中放入内容的自由度,或者说不会这样做。不要让我根据“孩子”进行自定义。
当他们位于两个不同的javascript文件中时,如何将“上下文”从提供者传递给消费者?代码在上面。我本质上需要将处理程序传递给每个孩子,并让它(也许,也许不是,具体取决于孩子)使用该处理程序告诉父项更新其数据。所有这些都在父级中使用{this.props.children}
时进行,以允许“外部代码”向父级组件“注入”所需的任何子级,并让他们使用或不使用父级的处理程序。
我仔细搜索了一下,发现了两个可能的解决方案,我都对其进行了测试,并且似乎都可以使用(用例有限)。当存在一层嵌套时,渲染道具和 React.CloneElement 似乎都能解决问题,因为我们可以直接渲染并向带有它们的孩子添加道具,但是当我们需要时要在多个级别进行道具钻取,介于两者之间的所有组件都必须实施相同的道具传递,然后再传递到意大利面条代码。仍在尝试尝试找到将上下文传递给子级以供在不同文件中使用的方法。
答案 0 :(得分:1)
请查看此代码belove。这也是我构建的示例项目的示例:https://codesandbox.io/s/5z62q8qnox
import React from 'react'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class ControllerForm extends React.Component {
static childContextTypes = {
onChange: PropTypes.func.isRequired
}
getChildContext() {
return {
onChange: this.handleOnChange
}
}
handleOnChange = (e) => {
console.log(e.target.value) //here is the place you have to implement
}
render() {
return (
<div class="container">
{this.props.children}
</div>
)
}
}
import React from 'react'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class LabeledControl extends React.Component {
static contextTypes ={
onChange : PropTypes.func.isRequired
}
render() {
return (
<div>
<div className="form-group">
<input className="form-control" type="text" onChange={this.context.onChange} />
</div>
</div>
)
}
}
function App() {
return (
<div className="App">
<ControllerForm>
<LabeledControl />
<LabeledControl />
</ControllerForm>
</div>
);
}
答案 1 :(得分:0)
看来Context
不是我应该使用的,相反,尽管我尽了最大努力来执行上下文,但render props
或React.cloneElement()
才是正确的解决方案。
父母的渲染:
{this.props.children.map((child, index) =>
React.cloneElement(child, { key: index, handler: handler })
)}
孩子的渲染:
return (
<div>
<span onClick={this.props.handler}>{passed.foo}</span>
</div>
);
通过这种方法,结构保持干净,处理程序得以传递。唯一的问题是,每个需要传递给它们的组件都必须实现这一点,但是与上下文相同,因为它不会导出到单独的文件中。