如何更新使用两个上下文使用者的组件的状态



我有一个类组件,它使用两个上下文,其中包含从对 REST API 的 API 调用生成的值。

我想做的是获取上下文值并使用它们来更新我的组件状态。

我像这样传递上下文值

<TextContext.Consumer>
{(textContext) => (
<UserContext.Consumer>
{(userConsumer) => {
const text = textContext.text;
const user = userConsumer.user;
if(text != null && user != null){
return (
<div className="md:flex max-w-2xl">
<div className="flex flex-col flex-1 md:pr-32">
<FuseAnimateGroup
enter={{
animation: "transition.slideUpBigIn"
}}
>
<div style={{paddingRight:"8px"}}>
<Typography variant="h4" >{text.TITLE_PAGE_PROFILE}</Typography>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
placeholder={text.PROFILE_EMAIL_PLACEHOLDER}
value = {user.email}
disabled
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment>
<IconButton>
<EmailIcon/>
</IconButton>
</InputAdornment>
)
}}
/>
</div>

<div style={{paddingRight:"8px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
placeholder={text.PROFILE_NAME}
value={user.name_user}
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<AccountCircle />
</InputAdornment>
)
}}
/>

</form>
</div>
<div style={{paddingRight:"8px"}}>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
value={user.address_user}
placeholder={text.PROFILE_ADDRESS_PLACEHOLDER}
fullWidth
margin="normal"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
/>
</div>
<div style={{paddingRight:"8px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
value={user.city_user}
label={text.PROFILE_CITY_PLACEHOLDER}
className={classes.textField}
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<LocationCityIcon/>
</InputAdornment>
)
}}
/>

</form>
</div>
<div>
<TextField
id="outlined-select-currency"
select
value={user.country_user}
label={text.PROFILE_COUNTRY_PLACEHOLDER}
InputProps={{
endAdornment: (
<InputAdornment>
<IconButton>
<FlagIcon/>
</IconButton>
</InputAdornment>
)
}}
fullWidth
style={{ margin: 8, paddingRight: 8}}
SelectProps={{
MenuProps: {
className: classes.menu,
},
}}

margin="normal"
variant="outlined"
/>
</div>
<div style={{padding:"10px"}}>
<Fab variant="contained" aria-label="delete" className={classes.fab}>
{text.PROFILE_CHANGE_PASSWORD_BUTTON_PLACEHOLDER}
</Fab>
</div>
<div style={{paddingRight:"8px"}}>
<Typography variant="h4" > {text.COMPANY_INFORMATION_TITLE}</Typography>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
placeholder={text.COMPANY_NAME_PLACEHOLDER}
value={user.name_company}
fullWidth
margin="normal"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
/>
</div>
<div style={{paddingLeft:"10px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
style={divStyle}
id="outlined"
label={text.COMPANY_EU_VAT_PLACEHOLDER}
value={user.vat_company}
className={classes.textField}
margin="normal"
variant="outlined"
/>
<TextField
style={div2Style}
id="outlined"
label={text.COMPANY_NUMBER_PLACEHOLDER}
value={user.registration_number_company}
className={classes.textField}
margin="normal"
variant="outlined"
/>
</form>
</div>
<div style={{paddingRight:"8px"}}>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
value={user.address_company}
placeholder={text.COMPANY_ADDRESS_PLACEHOLDER}
fullWidth
margin="normal"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
/>
</div>
<div style={{paddingRight:"8px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
label={text.COMPANY_CITY_PLACEHOLDER}
value={user.city_company}
className={classes.textField}
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<LocationCityIcon/>
</InputAdornment>
)
}}
/>
</form>
</div>
<div>
<TextField
id="outlined-select-currency"
select
label={text.COMPANY_COUNTRY_PLACEHOLDER}
fullWidth
style={{ margin: 8, paddingRight: 8}}
SelectProps={{
MenuProps: {
className: classes.menu,
},
}}
InputProps={{
endAdornment: (
<InputAdornment>
<IconButton>
<FlagIcon/>
</IconButton>
</InputAdornment>
)
}}
margin="normal"
variant="outlined"
/>
</div>
</FuseAnimateGroup>
</div>
<div className="flex flex-col md:w-320">
<FuseAnimateGroup
enter={{
animation: "transition.slideUpBigIn"
}}
>
<Button variant="contained"  size="large" color="default" className={classes.button}>
{text.UPDATE_BUTTON_TEXT}
</Button>
</FuseAnimateGroup>
</div>
</div>
);
} else return <div>Loading...</div>
}
}
</UserContext.Consumer>
)}
</TextContext.Consumer>

我试图通过做这样的事情来更新渲染中的状态

<TextContext.Consumer>
{(textContext) => (
<UserContext.Consumer>
{(userConsumer) => {
const text = textContext.text;
const user = userConsumer.user;
this.setState({
user:user,
text: text,
})
</UserContext.Consumer>
)}
</TextContext.Consumer>

此方法的问题在于它会引发"超出最大更新深度"错误。

我应该怎么做?

">

超出最大更新深度"错误。

不要setState()render()内。

我应该怎么做?

只需从中提取一个组件即可。

const User = (props) => {
return (
<>
<span>{props.user}</span>
<span>{props.text}</span>
</>
);
}
// in render
<TextContext.Consumer>
{(textContext) => (
<UserContext.Consumer>
{(userConsumer) => (
<User
text={textContext.text}
user={userConsumer.user}
/>
))}
</UserContext.Consumer>
)}
</TextContext.Consumer>

每次道具(用户、文本(更改时,<User />仍会重新渲染。

您无法更新渲染函数中的状态。

这样,您将处于渲染的无限循环中。每当更改触发渲染函数的状态时,都会再次更改状态,依此类推。

无论如何,您不需要将此状态存储在本地状态中即可使用它,您可以直接从上下文中使用它。

首先 - 你确定你真的需要在状态中存储上下文吗?我认为没有任何理由复制上下文(始终可用(来陈述。只需使用上下文中的值,而不是状态中的值。

但是如果你真的需要它,你不能在渲染函数中更新状态,因为它会导致无限更新循环。有一些选项可以这样做:

  • 提取组件:
return (
<TextContext.Consumer>
{({ text }) => (
<UserContext.Consumer>
({user}) => <ExtractedComponent text={text} user={user} />
</UserContext.Consumer>
)}
</TextContext.Consumer>
);

然后,您只需覆盖getDerrivedStateFromProps()ExtractedComponent就可以在道具更改时获得新状态。

  • [丑陋的方式] 在渲染函数中执行条件更新以防止无限循环:
if (state.user !== user || state.text !== text) {
this.setState({ user, text });
}
  • 也许你可以切换到带有钩子的功能组件:
const YourComponent = () => {
const { user } = useContext(UserContext);
const { text } = useContext(TextContext);
const [ state, setState ] = useState({});
useEffect(() => {
setState({ user, text });
}, [ user, text ]);
}

相关内容

最新更新