Reputation: 673
I have a class component which uses two contexts with the value generated from API calls to a REST API.
What I want to do is get the context values and use them to update my component state.
I'm passing the context values like so
<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>
I've tried to update the state inside the render by doing something like this
<TextContext.Consumer>
{(textContext) => (
<UserContext.Consumer>
{(userConsumer) => {
const text = textContext.text;
const user = userConsumer.user;
this.setState({
user:user,
text: text,
})
</UserContext.Consumer>
)}
</TextContext.Consumer>
The problem with this approach is that it throws the "Maximum update depth exceeded." error.
How should I go about this?
Upvotes: 3
Views: 206
Reputation: 1012
First of all - are you sure you really need to store context in state? I don't see any reason to copy context (which always available) to state. Just use values from context, not from state.
But if you really need it, you can't update state in render function, because it will cause the infinite update loop. There some options to do so:
return (
<TextContext.Consumer>
{({ text }) => (
<UserContext.Consumer>
({user}) => <ExtractedComponent text={text} user={user} />
</UserContext.Consumer>
)}
</TextContext.Consumer>
);
Then you just need to overrider getDerrivedStateFromProps()
for ExtractedComponent
to get new state when props changed.
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 ]);
}
Upvotes: 0
Reputation: 114
you can't update the state inside the render function.
Like that, you will be in the infinity loop of renders. Whenever you change the state that triggers the render function then you change the state again and so on.
anyway, you don't need to store this state inside the local state to use it, you can use it directly from the context.
Upvotes: 0
Reputation: 12174
"Maximum update depth exceeded." error.
Do not setState()
inside render()
.
How should I go about this?
Simply extract a component from it.
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 />
will still re-render every time the props (user, text) changes.
Upvotes: 1