I have 2 components, Index.js and Menu.js. Index.js contains Menu.js.
I have a state declared in the top level, Index.js. When you click on the edit icon existing in Index.js, it triggers a toggle on the css display attribute of the state variable. When this is toggled, it does not immediately show the item that now has a changed css value from display: none to display: block. However, if I click inside the area where the Menu.js component is, it then renders and shows the div (mainly because I have some functionality that forces a render with document.onmouseup). UseEffect was placed into Menu.js and is not triggered by the change. It too will only trigger if I click inside the component. Additionally, if no clicking is done inside of the component, it will be one step behind on the toggle.
Menu.js
useEffect(() => {
console.log("do something");
}, [form.display]);
return (
<div style={{ height: "500px", display: form.display }} id="editForm"></div>
);
Index.js
const [form, setForm] = useState({
display: "none",
});
const handleEditForm = () => {
var updateForm;
console.log("triggered");
if (form.display == "none") {
updateForm = form;
updateForm.display = "block";
setForm(updateForm);
} else {
updateForm = form;
updateForm.display = "none";
setForm(updateForm);
}
};
return (
<div>
<img
onClick={handleEditForm}
style={{
position: "relative",
top: "5px",
paddingLeft: "10px",
width: "30px",
}}
src="/public/static/images/edit_icon.svg"
alt="edit icon"
/>
</div>
);
Note: There is a conditional onm ouseDown, onm ouseMove and onm ouseUp function
CodePudding user response:
This is a basic concept of Javascript and React. React compares objects before deciding a state variables has changed. Since objects are compared with referential equality, your changing it (or mutating it, in JS terms) does not trigger a rerender.
As you can notice, your objects still point to the same address:
//according to your code
let form = { display : 'none' };
let updateForm;
updateForm = form;
updateForm.display = "block";
console.log(updateForm === form);
You need to create a new address. Notice how below two objects are not equal even when having same properties.
//How to create a different object
const updateForm = { display : "none" };
updateForm.display = "block";
const updateForm2 = { ...updateForm, display : "block"};
console.log(updateForm === updateForm2);
You have to do something similar. I have used spread operator but you can create your object again (since only one property). No need to create that extra variable
setForm({display : "none"});
setForm({display : "block"});
CodePudding user response:
This is because you are not mutating the state correctly.
In the code below you are assigning the form value to the temp value updateForm
. But that doesn't actually new value, as it's object and when you do it updateForm
and form
would have same reference
as object.
So setFor(updateForm)
would not work.
You need to assign new value to the setForm
function. Consider below code.
...
const handleEditForm = () => {
setForm({
...form,
display: form.display === 'block' ? 'none' : 'block'
})
};
...