Last active
February 8, 2018 20:01
-
-
Save joeldenning/f04f6acb9c2a966acf4e1d815e11c117 to your computer and use it in GitHub Desktop.
styled-components inside of render
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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