3

I have a component:

const MyComp : React.FC<{ editing?: Data }> = ({editing = { title = '', description = '', date: new Date() } }) => {
    const [data, setData] = useState<Data>({...editing})
    useEffect(() => setData({...editing}), [editing])
    return (/* use data inside here to render component */)
}

The problem is that the useEffect is looping, so React is detecting that the editing prop is changing every, the thing is that i use MyComp without any props like this:

<MyComp></MyComp>

This is really confusing me, i'm kinda lost now on how React works, any ideas on why is this happening?

1
  • 1
    If editing is not specified as a prop, the default will be used. And it'll be a different default instance for each render and that will see the effect run and that will set the state and that will render ... Commented Jan 7, 2020 at 21:16

2 Answers 2

8

Because editing is an object. Objects are compared by reference. If you don't pass the prop editing to the component, in each render, editing will receive a new link in memory, because you passing a default value to it. So useEffect will assume that dependencies have changed.

You can set primitive types to dependencies.

const MyComp : React.FC<{ editing?: Data }> = ({editing = { title = '', description = '', date: new Date() } }) => {
    const [data, setData] = useState<Data>({...editing})
    useEffect(() => setData({...editing}), [editing.title, editing.description, editing.date.getTime()])
    return (/* use data inside here to render component */)
}
Sign up to request clarification or add additional context in comments.

2 Comments

Wow that was it, i was getting strange behaviors if i changed from {...editing} to just editing, really didn't get why, but i changed the code to this: editing = undefined and useState(editing || { /*default data here*/ }), inside useEffect just check if editing isn't undefined and setData, works fine now, thanks for the answer
This is correct. Another alternative would be to declare the default object as a const outside of the function. This would lead to the same object reference being used each render
4

It happens because useEffect uses shallow comparison. You can either destructure the object

const {param1, param2} = editing;
useEffect(() => {
//logic goes here
}, [param1, param2]);

Or write a custom hook that uses Lodash isEqual for example

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.