我是一个非常新的反应者,我正在努力解决以下问题。我在react中创建了一个表单,该表单包含一个下拉列表。我需要在多个页面中重复使用该下拉菜单,所以我认为将其做成完全负责获取所有数据的组件。
在表单组件中,我获取了所有数据,并且这些字段之一是selectedServerDataId。 selectId字段包含需要在下拉列表中选择的值的ID。
<snip includes/>
class Arrival extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
formData: {}
};
}
async componentDidMount() {
await fetch(urls.addEditUrl)
.then(response => response.json())
.then(data => {
this.setState({ formData: data });
});
}
<snip some setstate helpers/>
render() {
const { formData } = this.state;
return (
<Form >
<Selector label="select"
onChange={this.UpdateFormSelectData.bind(this, 'selectedServerDataId')}
value={formData.selectedServerDataId}/>
<DateItem label="date"
allowClear={true}
value={formData.date}
onChange={this.updateFormDateData.bind(this, 'date')}/>
<snip more fields.../>
</Form>);
}
}
export default Arrival;
父组件中的访存将检索编辑表单的数据,其中包括selectedServerDataId。我的子组件如下所示:
<snip usings />
class ServerDataSelector extends React.Component {
constructor(props) {
super(props);
this.state = {
options: [],
};
}
async componentDidMount() {
await fetch(URLS.GetServerData)
.then(response => response.json())
.then(data => {
this.setState({ options: data });
});
}
render() {
const { options } = this.state;
return (
<FormItem {...formItemLayout} label={t(this.props.label)} >
<Select setValue={this.props.onChange} getValue={() => this.props.value} selectedOption={this.props.value} name={this.props.label}>
{options.map(d => <Option key={d.id} value={d.id}>{d.value}</Option>)}
</Select>
//TODO add model window to manipulate list.
</FormItem>);
}
}
export default ServerDataSelector
ServerDataSelector { Selector }
当前,当页面呈现时,我首先在下拉列表中看到所选值的ID,然后一秒钟之后,我看到了实际的所选值标签。
是否有一种方法可以确保仅在子项完全完成加载时才渲染父组件?
答案 0 :(得分:0)
这里的主要问题是,您有两个组件具有严格的呈现层次关系(父级->子级),但具有相同的数据提取责任。
这导致您必须对多个组件之间以特定顺序进行的数据检索作出反应,这是一个非同寻常的并发问题。一种解决方案是使用共享状态容器(使用Redux或Context API),并使两个组件将其结果保存到此共享状态存储桶中,然后每当存储桶中包含所有数据时,都将其呈现(例如,直到您可以显示微调框)。
一种更简单的解决方案是将获取操作移至一个组件中,完全消除这种拆分的数据获取责任,并允许组件获取数据也控制渲染。
一种方法是获取父级中的所有数据,并使子级成为接收数据时呈现的纯组件:
class Arrival extends React.Component {
// ...
async componentDidMount() {
const [formData, options] = await Promise.all([
fetch(urls.addEditUrl).then(response => response.json()),
fetch(URLS.GetServerData).then(response => response.json())
]);
this.setState({ formData, options });
}
render() {
const { formData, options } = this.state;
return (
<Form>
{/* ... */}
{formData &&
options && (
<Selector
label="select"
options={this.state.options} {/* pass the options here */}
onChange={this.UpdateFormSelectData.bind(
this,
"selectedServerDataId"
)}
value={formData.selectedServerDataId}
/>
)}
</Form>
);
}
}
Selector
现在只是表示性组件,不需要状态:
const ServerDataSelector = props => (
<FormItem {...formItemLayout} label={t(this.props.label)}>
<Select
setValue={this.props.onChange}
getValue={() => this.props.value}
selectedOption={this.props.value}
name={this.props.label}
>
{this.props.options.map(d => ( {/* use props.options */}
<Option key={d.id} value={d.id}>
{d.value}
</Option>
))}
</Select>
</FormItem>
);
您可以选择显示微调器或加载指示器:
...
{
formData && options ? (
<Selector
label="select"
onChange={this.UpdateFormSelectData.bind(this, "selectedServerDataId")}
options={this.state.options}
value={formData.selectedServerDataId}
/>
) : (
<Spinner />
);
}
或者,您可以让子级获取所有数据,而父级将仅呈现该子级。
class ServerDataSelector extends React.Component {
// ...
async componentDidMount() {
const [formData, options] = await Promise.all([
fetch(urls.addEditUrl).then(response => response.json()),
fetch(URLS.GetServerData).then(response => response.json())
]);
this.setState({
value: formData.selectedServerDataId,
options
});
}
render() {
return (
<FormItem {...formItemLayout} label={t(this.props.label)}>
<Select
setValue={this.props.onChange}
getValue={() => this.state.value} {/* use the value from the state */}
selectedOption={this.state.value} {/* use the value from the state */}
name={this.props.label}
>
{this.state.options.map(d => (
<Option key={d.id} value={d.id}>
{d.value}
</Option>
))}
</Select>
</FormItem>
);
}
}
您还可以在子级中添加一个微调框,以防止页面出现混乱并允许使用更好的UX:
render() {
if (!this.state.value || !this.state.options) {
return <Spinner />;
}
return (
<FormItem {...formItemLayout} label={t(this.props.label)}>
<Select
setValue={this.props.onChange}
getValue={() => this.state.value}
selectedOption={this.state.value}
name={this.props.label}
>
{this.state.options.map(d => (
<Option key={d.id} value={d.id}>
{d.value}
</Option>
))}
</Select>
</FormItem>
);
}
答案 1 :(得分:0)
经过多次尝试和错误,我找到了一种解决方法。
在我的父状态下,添加了以下对象:
this.state = {
formData: null,
loadedComponents: {
parentComponentIsLoaded: false,
childComponent1IsLoaded: false,
childComponent2IsLoaded: false,
}
};
然后我在父母中添加了以下2种方法
componentLoaded = (field, data) => {
const { loadedComponents } = this.state;
var fieldName = field + "IsLoaded";
this.setState({ loadedComponents: { ...loadedComponents, [fieldName]: true } });
}
allComponentsLoaded() {
const { loadedComponents } = this.state;
console.log(loadedComponents);
for (var o in loadedComponents) {
if (!loadedComponents[o]) return false;
}
return true;
}
将render方法更改为此:
return (
formData ?
<Form className={this.allComponentsLoaded() ? '' : 'hidden'} >
<ChildComponet1 {someprops} isLoaded={this.componentLoaded}/>
<ChildComponent2 {someprops} isLoaded={this.componentLoaded}/>
<snip />
</Form> : <div />);
并在获取数据后在childs componentDidMount方法中添加以下行。
this.props.isLoaded('childComponet1', true)
也许这是非常糟糕的反应设计,您是否应该实际使用MEM035的答案,但这确实有用