I have a MaterialUI dialog that has a few text fields, drop downs, and other things on it. Some of these elements need to be set to some value every time the dialog opens or re-opens. Others elements cannot be loaded until certain conditions exist (for example, user data is loaded).
For the 'resetting', I'm using the onEnter function. But the onEnter function doesn't run until entering (duh!)... but the render function, itself, still does - meaning any logic or accessing javascript variables in the JSX will still occur. This leaves the 'onEnter' function ill-equipped to be the place I set up and initialize my dialog.
I also can't use the constructor for setting/resetting this initial state, as the data I need to construct the state might not be available at the time the constructor loads (upon app starting up). Now, I could super-complicate my JSX in my render function and make conditionals for every data point... but that's a lot of overhead for something that gets re-rendered every time the app changes anything. (the material UI dialogs appear run the entire render function even when the 'open' parameter is set to false).
What is the best way to deal with initializing values for a material ui dialog?
Here is a super-dumbed-down example (in real life, imagine getInitialState is a much more complex, slow, and potentially async/network, function) - let's pretend that the user object is not available at app inception and is actually some data pulled or entered long after the app has started. This code fails because "user" is undefined on the first render (which occurs BEFORE the onEnter runs).
constructor(props) {
super(props);
}
getInitialState = () => {
return {
user: {username: "John Doe"}
}
}
onEnter = () => {
this.setState(this.getInitialState())
}
render() {
const { dialogVisibility } = this.props;
return (
<Dialog open={dialogVisibility} onEnter={this.onEnter}>
<DialogTitle>
Hi, {this.state.user.username}
</DialogTitle>
</Dialog> );
}
My first instinct was to put in an "isInitialized" variable in state and only let the render return the Dialog if "isInitialized" is true, like so:
constructor(props) {
super(props);
this.state = {
isInitialized: false
};
}
getInitialState = () => {
return {
user: {username: "John Doe"}
}
}
onEnter = () => {
this.setState(this.getInitialState(),
() => this.setState({isInitialized:true})
);
}
render() {
const { dialogVisibility } = this.props;
if(!this.state.isInitialized) {
return null;
}
return (
<Dialog open={dialogVisibility} onEnter={this.onEnter}>
<DialogTitle>
Hi, {this.state.user.username}
</DialogTitle>
</Dialog> );
}
As I'm sure you are aware... this didn't work, as we never return the Dialog in order to fire the onEnter
event that, in turn, fires the onEnter
function and actually initializes the data. I tried changing the !this.state.inInitialized
conditional to this:
if(!this.state.isInitialized) {
this.onEnter();
return null;
}
and that works... but it's gives me a run-time warning: Warning: Cannot update during an existing state transition (such as within
render). Render methods should be a pure function of props and state.
That brought me to a lot of reading, specifically, this question: Calling setState in render is not avoidable which has really driven home that I shouldn't be just ignoring this warning. Further, this method results in all the logic contained in the return JSX to still occur... even when the dialog isn't "open". Add a bunch of complex dialogs and it kills performance.
Surely there is a 'correct' way to do this. Help? Thoughts?