Думаю, что опираться на три точки(список продуктов, один продукт, еще и локальный стейт для редактируемого продукта) с идентичными данными крайне излишне, т.к. создаётся путаница с синхронизацией. Считаю, что нужно опираться только на список продуктов из редакса и доставать из него нужные данные через селекторы. Для редактирования можно использовать временный локальный стейт, который при желании можно сбросить и вернуться к исходным данным.
Имена немного изменил, чтобы было более понятно. Но вы, если что, спрашивайте, постараюсь помочь.
// Component
const ProductEdit = () => {
const [editableData, setEditableData] = useState({});
const { id } = useParams();
// пытаемся достать продукт по id из списка
const selectedProduct = useSelector(state => (
state.products.find(product => product.id === id)
));
useEffect(() => {
// если продукт был найден, сразу заносим его в локальный стейт
if (selectedProduct) {
setEditableData(selectedProduct);
}
// иначе получаем данные с бэкэнда(минуя редакс) и затем заносим в локальный стейт
// этот кейс будет срабатывать в случае если страница была перезагружена
else {
API.fetchProductById(id).then(fetchedProduct => setEditableData(fetchedProduct));
}
}, [selectedProduct, id]);
const onChange = ({ target: { name, value } }) => {
setEditableData(currentData => ({ ...currentData, [name]: value }));
};
const dispatch = useDispatch();
const onSubmit = e => {
e.preventDefault();
dispatch(updateProduct({ id, ...editableData }));
};
return (
<form onSubmit={onSubmit}>
<input type='text' name='name' value={editableData.name || ''} onChange={onChange} />
<input type='text' name='text' value={editableData.text || ''} onChange={onChange} />
<input type='text' name='price' value={editableData.price || ''} onChange={onChange} />
<button>update product</button>
</form>
);
};
// Thunk
const updateProduct = product => async (dispatch, getState) => {
try {
await API.updateProduct(product);
// проверяем на наличие списка продуктов, может не быть если страница перезагружалась
if (getState().products.length) {
dispatch(mergeUpdatedProduct(product));
// ловим type этого экшна в редьюсере и перезаписываем старый продукт в списке
}
// если же список пустой, то при возврате на страницу показа продуктов заново получаем все данные
// к тому моменту обновлённый продукт уже успешно будет проживать в бд
} catch (error) {
// не удалось обновить продукт
}
};