Hi Kent - I'm having some trouble seeing the optimization discussed in exercise 3 for preventing unnecessary re-renders of the consumers. It could be due to a misunderstanding by me of how those consumers work. To test it out I added a simple counter button that updates the state of the Usage component to trigger a re-render. I also added a console.log within Toggle.Button to check when a rendering happens.
// Flexible Compound Components with context
import React from 'react'
import {Switch} from '../switch'
const ToggleContext = React.createContext();
function ToggleConsumer(props) {
return (
<ToggleContext.Consumer>
{
(context) => {
if (!context) {
throw new Error('Toggle compound components must be rendered within the Toggle component');
}
return props.children(context);
}
}
</ToggleContext.Consumer>
);
}
class Toggle extends React.Component {
static On = (props) => (
<ToggleConsumer>
{({ on }) => on ? props.children : null}
</ToggleConsumer>
);
static Off = (props) => (
<ToggleConsumer>
{({ on }) => on ? null : props.children}
</ToggleConsumer>
);
static Button = (props) => {
return (
<ToggleConsumer>
{({ on, toggle }) => {
console.log('rendering button');
return <Switch on={on} onClick={toggle} {...props} />
}}
</ToggleConsumer>
);
}
toggle = () =>
this.setState(
({on}) => ({on: !on}),
() => this.props.onToggle(this.state.on),
)
state = { on: false, toggle: this.toggle }
render() {
// UN-OPTIMIZED
// return (
// <ToggleContext.Provider value={{on: this.state.on, toggle: this.toggle }}>
// {this.props.children}
// </ToggleContext.Provider>
// );
// OPTIMIZED
return (
<ToggleContext.Provider value={this.state}>
{this.props.children}
</ToggleContext.Provider>
);
}
}
class Usage extends React.Component {
static title = 'Flexible Compound Components';
state = { count: 0 };
increment = () => {
this.setState((prevState) => ({ count: prevState.count + 1}));
}
onToggle = (...args) => console.log('onToggle', ...args);
render() {
const onToggle = this.props.onToggle || this.onToggle;
return (
<React.Fragment>
<div>{this.state.count}</div>
<button onClick={this.increment}>+</button>
<Toggle onToggle={onToggle}>
<div>
<Toggle.Button />
</div>
<Toggle.On>The button is on</Toggle.On>
<Toggle.Off>The button is off</Toggle.Off>
</Toggle>
</React.Fragment>
);
}
}
export {Toggle, Usage as default}
My expectation was that with the optimization of passing this.state I'd no longer see rendering button logged every time I increment my counter. But it still is getting logged. If there is a better forum for this question please let me know!
I believe that if you make the button a pure component (either via extending PureComponent, or using the upcoming React.pure API) then that will get you what you're looking for :)
Hi Kent - I'm having some trouble seeing the optimization discussed in exercise 3 for preventing unnecessary re-renders of the consumers. It could be due to a misunderstanding by me of how those consumers work. To test it out I added a simple counter button that updates the state of the Usage component to trigger a re-render. I also added a
console.log
withinToggle.Button
to check when a rendering happens.My expectation was that with the optimization of passing
this.state
I'd no longer seerendering button
logged every time I increment my counter. But it still is getting logged. If there is a better forum for this question please let me know!