Here are my notes on a few of my favorite posts on Overreacted, the blog of the React Core team’s Dan Abramov.
Writing Resilient Components
- Things that don’t matter: too much deference on opinionated style guides/lint configs, cargo cult rules like “don’t call
setState
incomponentDidMount
. The former often puts too much emphasis on aesthetics and just creates conflict and stress without any improvement in maintainability.- It’s really just important to have some set of rules, so that auto formatting is always consistent
Don’t stop the data flow
- For derived state, memoization is cleaner than
getDerivedStateFromProps
.componentDidUpdate
is an option, but causes double renders. - Make sure that side effects are repeated on prop or state updates (eg, calls in
componentDidMount
may have to be repeated incomponentDidUpdate
)- Alternatively, the
useEffect
hook combines all lifecycle methods and makes prop, state dependencies explicit
- Alternatively, the
- Be careful when comparing function props across updates: methods retain their identity across renders, but functions may not. The
useCallback
hook helps make this more explicit by memoizing closures so that their identity changes iff an element in the dependency array changes.
Always be ready to render
- Derived state can also have the “blowing away” problem: local state updates are overriden by props updates
- Fix this by making components fully controlled, limiting state updates to a container component
No component is a singleton
- Any component, even something like a nav bar, might need to be rendered twice
- Avoid global state for that reason
Keep the Local State Isolated
- What state should remain local vs. in a central (eg, Redux) store?
- Try asking this question: if a component is rendered twice, what state should be updated in the other component when updated in one?
- Local state should generally be limited to presentational state (eg, are these comments expanded?) or ephemeral state (eg, text in a comment field that hasn’t been submitted)
Why do we write super(props)?
- ES6 classes require super() to be called in the first line of the constructor (if at all), so that super class fields are initialized if you want to call super class methods
- React requires super(props) mainly so that this.props is accessible in the constructor
- But it’s not strictly necessary, since React initializes this.props after instantiating your opponent anyway
Why do React elements have typeof property?
- React escapes strings to prevent XSS attacks
- Of course, it’s still vulnerable to attacks under certain conditions - eg, this renders arbitrary html:
<div dangerouslySetInnerHtml: {_html: “<script>stealPassword() </script>”}>
Could happen if you spread user input on a div
Could also happen if your server serves JSON where you expect text, and you get his:
let expectedTextButGotJSON = {
type: 'div',
props: {
dangerouslySetInnerHTML: {
__html: '/* put your exploit here */'
},
},
// ...
};
let message = { text: expectedTextButGotJSON };
// Dangerous in React 0.13
<p>
{message.text}
</p>
See: http://danlec.com/blog/xss-via-a-spoofed-react-element
React protects against this by adding the typeof: symbol(‘react.elemt’) to React elements, which can’t be replicated in JSON. So, it’s a very specific fix - but it does enable the typeof function on React elements
Why do hooks rely on call order?
useState can be used multiple times to create multiple state variables:
const [a, setA] = useState(1);
const [b, setB] = useState(2);
const [b, setB] = useState(2);
useState(a) {
let current = a;
let setA = (a) => {
current = a;
};
return current;
}
This (supporting multiple useState calls) is mostly useful so that we can extract stateful logic into custom hooks that use useState:
This useWindowWidth hook outputs the window width as it changes with lifecycle updates (though apparently the same as just using window.innerWidth?):
function useWindowWidth() {
// Declare some state and effects in a custom Hook
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
// ...
});
return width;
}
Dan presents a number of solutions that involve passing an identifying key, and points out how each in turn fails to meet these requirements, or requires a lot of additional setup.