Reputation: 1504
I am following along with a video tutorial on using React. The presenter is currently detailing how to add a toggle button to a UI. They said to give it a go first before seeing how they do it, so I implemented it myself. My implementation was a little different to theirs, just the handler was different; but it does seem to work.
Can anyone with more experience using React tell me, is my toggleSideDrawerHandler wrong in some way? Or is it a valid shorter way of setting the state that depends on a previous state?
My implementation:
//Layout.js
class Layout extends Component {
state = {
showSideDrawer: false
};
toggleSideDrawerHandler = prevState => {
let newState = !prevState.showSideDrawer;
this.setState({ showSideDrawer: newState });
};
closeSideDrawerHandler = () => {
this.setState({ showSideDrawer: false });
};
render() {
return (
<Fragment>
<Toolbar drawerToggleClicked={this.toggleSideDrawerHandler} />
<SideDrawer
open={this.state.showSideDrawer}
close={this.closeSideDrawerHandler}
/>
<main className={styles.Content}>{this.props.children}</main>
</Fragment>
);
}
}
//Toolbar.js
const toolbar = props => (
<header className={styles.Toolbar}>
<DrawerToggle clicked={props.drawerToggleClicked} />
<div className={styles.Logo}>
<Logo />
</div>
<nav className={styles.DesktopOnly}>
<NavItems />
</nav>
</header>
);
Tutorial implementation:
toggleSideDrawerHandler = () => {
this.setState(prevState => {
return { showSideDrawer: !prevState.showSideDrawer };
});
};
Upvotes: 0
Views: 112
Reputation: 1577
You are using this:
toggleSideDrawerHandler = prevState => {
let newState = !prevState.showSideDrawer;
this.setState({ showSideDrawer: newState });
};
This is a conventional way to update state in react, where we are defining the function and updating state inside. Though you are using term prevState but it doesn't holds any value of components states. When you call toggleSideDrawerHandler method you have to pass value and prevState will hold that value. The other case as tutorial is using:
toggleSideDrawerHandler = () => {
this.setState(prevState => {
return { showSideDrawer: !prevState.showSideDrawer };
});
};
This is called functional setStae way of updating state. In this function is used in setState methods first argument. So prevState will have a value equal to all the states in the component.Check the example below to understand the difference between two:
// Example stateless functional component
const SFC = props => (
<div>{props.label}</div>
);
// Example class component
class Thingy extends React.Component {
constructor() {
super();
this.state = {
temp: [],
};
}
componentDidMount(){
this.setState({temp: this.state.temp.concat('a')})
this.setState({temp: this.state.temp.concat('b')})
this.setState({temp: this.state.temp.concat('c')})
this.setState({temp: this.state.temp.concat('d')})
this.setState(prevState => ({temp: prevState.temp.concat('e')}))
this.setState(prevState => ({temp: prevState.temp.concat('f')}))
this.setState(prevState => ({temp: prevState.temp.concat('g')}))
}
render() {
const {title} = this.props;
const {temp} = this.state;
return (
<div>
<div>{title}</div>
<SFC label="I'm the SFC inside the Thingy" />
{ temp.map(value => ( <div>Concating {value}</div> )) }
</div>
);
}
}
// Render it
ReactDOM.render(
<Thingy title="I'm the thingy" />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
So depending on requirement you will use one of the two ways to update the state.
Upvotes: 1
Reputation: 1082
You should stick to the tutorial implementation. There is no point in passing component state to the children and then from them back to the parents. Your state should be only in one place (in this case in Layout
).
Child components should be only given access to the information they need which in this case is just showSideDrawer
.
Upvotes: 1
Reputation: 1377
Your solution works, but I guess in the part, where you call the toggleSideDrawerHandler
you probably call it like
() => this.toggleSideDrawerHandler(this.state)
right?
If not, can you please paste the rest of your code (especially the calling part) to see where you get the prevState
from?
This works, because you pass the old state to the method. I would personally prefer the tutorials implementation, because it takes care of dependencies and the "user" (the dev using it) doesn't need to know anything about the expected data.
With the second implementation all you need to do is call the function and not think about getting and passing the old state to it.
Update after adding the rest of the code:
I think the reason, why it works is because the default value for your parameter is the one passed by the event by default, which is an event
object.
If you use prevState.showSideDrawer
you are calling an unknown element on this event object, that will be null
.
Now if you use !prevState.showSideDrawer
, you are actually defining it as !null
(inverted null/false), which will be true.
This is why it probably works.
Maybe try to toggle your code twice, by showing and hiding it again. Showing it will probably work, but hiding it again will not. This is why the other code is correct.
Upvotes: 1