-
Notifications
You must be signed in to change notification settings - Fork 49.1k
Description
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
The new guard against a direct call to setState
inside of an effect (#15184) seems to prevent a class of patterns where the value being set is dependent on something other than props. For example, the rule disallows storing a value read from the DOM via a ref (see below).
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
function MeasuredButton(props) {
const buttonRef = useRef(null)
const [buttonWidth, setButtonWidth] = useState(0)
useLayoutEffect(() => {
if (buttonRef.current) {
// we rely on the same value bailout to avoid an infinite loop
setButtonWidth(buttonRef.current.clientWidth)
// we could bail out explicitly instead:
// const {clientWidth} = buttonRef.current
// if (clientWidth !== buttonWidth) setButtonWidth(clientWidth)
// but the linter would still disallow it
}
})
return (
<>
<button ref={buttonRef}>{props.children}</button>
Button width: {buttonWidth}
</>
)
}
This code yields the error:
React Hook useLayoutEffect contains a call to 'setButtonWidth'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [] as a second argument to the useLayoutEffect Hook.
The auto-fix breaks the component because the width no longer updates on subsequent renders.
What is the expected behavior?
Basically, the guard assumes that the infinite loop problem can always be solved by adding a dependency array. This is true when setting a value derived from props (such as data returned from a request based on a prop), but not when the source of the value can only be retrieved inside of the effect (such as a DOM measurement). In the latter case, an infinite loop has to be avoided by adding a condition or relying on the same value bailout.
Is this known and/or intentional? I notice that #15184 considered early returns, which would help.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React: 16.8.6
eslint-plugin-react-hooks: 1.6.0