//Wrap functional component Details in React.memo
const Details = React.memo(function Details({
formData: { title, description },
onChange,
}) {
console.log('render details');
return (
<React.Fragment>
<input
value={title}
onChange={({ target: { value } }) =>
onChange(value, 'details', 'title')
}
/>
<input
value={description}
onChange={({ target: { value } }) =>
onChange(value, 'details', 'description')
}
/>
</React.Fragment>
);
});
//Make Contact also pure with React.memo
const Contact = React.memo(function Contact({
formData: { name, number },
onChange,
}) {
console.log('render contact');
return (
<React.Fragment>
<input
value={name}
onChange={({ target: { value } }) =>
onChange(value, 'contact', 'name')
}
/>
<input
value={number}
onChange={({ target: { value } }) =>
onChange(value, 'contact', 'number')
}
/>
</React.Fragment>
);
});
const Create = () => {
const [formData, setFormData] = React.useState({
details: {
title: '',
description: '',
},
contact: {
name: '',
number: '',
},
});
//only create onChange once (empty dependencies for useCallback)
const onChange = React.useCallback(
(value, tab, field) => {
//you were mutating and not using callback for state
// setting causing unnecessary dependency for useCallback
setFormData(current => ({
...current,
[tab]: {
...current[tab],
[field]: value,
},
}));
},
[]
);
return (
<React.Fragment>
{/* instead of passing the entire formData, only pass
the part that it actually needs so when Contact changes
Details isn't re rendered because you're passing an object
that has changed you pass object containing contact to details
and object contact details to details */}
<Details
formData={formData.details}
onChange={onChange}
/>
<Contact
formData={formData.contact}
onChange={onChange}
/>
</React.Fragment>
);
};
ReactDOM.render(
<Create />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>