我有一个react组件,我想显示一条消息,告诉用户它正在与服务进行通信,并在完成后删除消息。我似乎无法让它发挥作用。当我点击按钮时,所有孩子都会显示," loading ..."并且消息永远不会消失
class RecipeList extends Component {
constructor() {
super();
this.state = { Names: [], loadingMessage: "" };
}
componentDidMount() {
const settings = appConfig;
fetch(settings.RestServerLocation + "/Api/recipe", {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(result => {
return result.json();
})
.then(data => {
this.setState({ Names: data })
});
}
render() {
const recipeListItems = this.state.Names.map((recipeName) =>
<li key={recipeName.Name.toString()}className="row" item={recipeName.Name}>
<Col xs="12" md="6">{recipeName.Name}</Col>
<Col xs="12" md="6">{this.state.loadingMessage}
<Button size="sm" onClick={() => this.props.viewclick(this, recipeName.Id)}>View recipe</Button>
</Col>
</li>
);
return (
<div>
<Card className="card-modified">
<CardHeader>Recipe List</CardHeader>
<CardBody>
<CardText>
<ul>
{recipeListItems}
</ul>
</CardText>
</CardBody>
</Card>
</div>
);
}
};
它的父母有代码
recipeListViewClickHandler = (w, id) => {
const settings = appConfig;
w.state.loadingMessage = "loading..."
fetch(settings.RestServerLocation + "/Api/recipe/" + id)
.then(result => {
return result.json();
})
.then(data => {
console.log(data);
//const foo = data;
this.setState({ Recipe: data })
w.state.loadingMessage = "complete";
}
)
.catch(e => {
console.log(e)
return e;
});
}
以及渲染中的此代码
<Col xs="12" lg="8"><RecipeList addclick={this.recipeListAddClickHandler} viewclick={this.recipeListViewClickHandler} />
答案 0 :(得分:1)
在父级中设置子组件的状态是一种反模式,但是通过调用w.state.loadingMessage
,您不会告诉组件更新,因此它永远不会知道改变。 setState()
函数将在告知组件更新时设置state
属性。你可能能够逃脱w.setState({loadingMessage: "complete"})
,但你真的不应该这样做。
如果要设置来自父级的加载消息,请将状态设置为父级(this.setState({loadingMessage: "complete"})
)而不是子级,然后将其作为子级传递给子级。一个财产而不是。
render() {
const {loadingMessage} = this.state;
return <Col xs="12" lg="8">
<RecipeList
addclick={this.recipeListAddClickHandler}
viewclick={this.recipeListViewClickHandler}
loadingMessage={loadingMessage}
/>
</Col>;
}
修改强>
根据你的评论,我已经意识到你的方式有点倒退。在列表组件中,您将从API中获取集合,但是从父项获取实例数据。你在这里缺少一个子组件。 ListItem
应该被提取为一个组件,并且可以为自己管理状态:)
class RecipeListItem extends Component {
constructor() {
super();
this.setState({loadingMessage: ""});
}
handleClick = () => {
const {recipeName} = this.props;
const settings = appConfig;
this.setState({loadingMessage: "loading..."});
fetch(`${settings.RestServerLocation}/Api/recipe/${recipeName.id}`)
.then(result => result.json())
.then(data => {
console.log(data);
this.setState({loadingMessage: "complete"});
})
.catch(e => {
console.log(e);
this.setState({loadingMessage: "ERROR"});
return e;
});
}
render() {
const {recipeName} = this.props;
return <li
key={recipeName.Name.toString()}
className="row"
item={recipeName.Name}
>
<Col xs="12" md="6">{recipeName.Name}</Col>
<Col xs="12" md="6">
{this.state.loadingMessage}
<Button size="sm" onClick={this.handleClick}>
View recipe
</Button>
</Col>
</li>;
}
}
然后,在List组件中,只使用子项:
render() {
const recipeListItems = this.state.Names.map((recipeName) =>
<RecipeListItem recipeName={recipeName} />
);
return (
<div>
<Card className="card-modified">
<CardHeader>Recipe List</CardHeader>
<CardBody>
<CardText>
<ul>
{recipeListItems}
</ul>
</CardText>
</CardBody>
</Card>
</div>
);
}
答案 1 :(得分:0)
您正在从父组件内的函数设置子组件的state
。在React中,组件的state
不应该变异,而应该只使用this.setState
方法设置。因此,您可以将代码更改为
w.setState({loadingMessage:'complete'});
....
w.setState({loadingMessage:'loading...'});
并检查它是否有效。
但我建议您重构代码,以便
state
state
与props
一起流入子组件以及任何必要的操作(函数)。如果不查看更多代码,就不可能进行这样的重构。