Skip to content

Instantly share code, notes, and snippets.

@joeldenning
Last active February 8, 2018 20:01
Show Gist options
  • Save joeldenning/f04f6acb9c2a966acf4e1d815e11c117 to your computer and use it in GitHub Desktop.
Save joeldenning/f04f6acb9c2a966acf4e1d815e11c117 to your computer and use it in GitHub Desktop.
styled-components inside of render
/*
The goal of the following code is to render the text “Copied!” when you click on a div.
After two seconds, you want to change the text to say “Something else.”
However, there is a bug which makes “Something else” never render. The reason why is that
React considers the StyledDiv to be a new type of element every time that Parent rerenders.
And one of React's reconciliation heuristics is to unmount/remount whenever the top level
returned child is a different type of element. So by the time the setTimeout happens, we are calling
setState on an already unmounted component. A new Child component has been created, with the initial state saying
"Copied!"
See https://github.com/facebook/react/blob/080db9d3a72bdad67608c8383f99678462b31082/src/renderers/shared/shared/shouldUpdateReactComponent.js#L37 for
where this is done in React@15 (`.type` is the constructor function, which is different each time because styled.div returns
a new function each time). I believe the same heuristic is used in React@16 as well
*/
class Parent extends React.Component {
state = {
showText: false,
}
render() {
const StyledDiv = styled.div`
background-color: navajowhite;
`
return (
<StyledDiv>
<Child showText={this.state.showText} sayCopied={() => this.setState({showText: true})} />
</StyledDiv>
)
}
}
class Child extends React.Component {
state = {
text: 'Copied!',
}
render() {
return (
<div onClick={this.sayCopied}>
{this.props.showText ? this.state.text : ''}
</div>
)
}
sayCopied = () => {
this.props.sayCopied()
setTimeout(() => {
this.setState({text: 'Something else'})
}, 2000)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment