Reputation: 1566
I'm new to reactjs and that's why it comes naive to you.
I need to update the value of a map in which its keys are unknown.
const App = () => {
const [storeMap, setStoreMap] = useState(new Map());
let _tmpMap = new Map();
return (<>
{Object.keys({ key1: "hey", key2: "you" }).map((item) => {
return (
<button
value={item}
key={item}
onClick={(e) => {
_tmpMap.set(item, e.target.value);
console.log(..._tmpMap); // {1}
setStoreMap(_tmpMap);
}}
>
{item}
</button>
);
// return <i key={item}>KJ </i>;
})}
</>)
}
What I am expecting to see in the above code after clicking both buttons is:
/* {1} */
console.log(..._tmpMap)
//i expect this: {key1:"key1" , key2:"key2"}
What I see in reality is {key1:"key1"}
after pressing key 1 and { key2:"key2"}
after pressing key 2
My question is:
How can I update storeMap while preserving its previous entries?
Upvotes: 0
Views: 2388
Reputation: 41
storeMap.set() updates the map and setStoreMap sets the state.
React compares the references of the new and old Map which, in this case, share the same value.
If you want React to "know" about the update you will need to pass to setStoreMap a clone of the Map instead of a copy of the old reference, you can do that by creating a new Map.
I believe you can drop the use of _tmpMap
.
const App = () => {
const [storeMap, setStoreMap] = useState(new Map());
const updateStoreMap = (k, v) => {
// pass a clone of storeMap
setStoreMap(new Map(storeMap.set(k, v)));
};
return (
<>
{Object.keys({ key1: 'hey', key2: 'you' }).map((item) => {
return (
<button
value={item}
key={item}
onClick={(e) => {
updateStoreMap(item, e.target.value);
}}
>
{item}
</button>
);
// return <i key={item}>KJ </i>;
})}
</>
);
};
Upvotes: 1
Reputation: 9354
When you call setStoreMap
, the component rerenders and _tmpMap
evaluates to a new Map again. The Map you updated belongs to the previous render and cannot be accessed. Anything you wish to preserve between renders has to be in state or a ref, so, you could do something like this:
const App = () => {
const [storeMap, setStoreMap] = useState(new Map());
let _tmpMap = useRef(new Map());
return (<>
{Object.keys({ key1: "hey", key2: "you" }).map((item) => {
return (
<button
value={item}
key={item}
onClick={(e) => {
_tmpMap.current.set(item, e.target.value);
setStoreMap(new Map(_tmpMap.current));
}}
>
{item}
</button>
);
// return <i key={item}>KJ </i>;
})}
</>)
}
However, it's generally advised not to use Maps with React as they are mutable, and React will have no way of knowing when one is mutated. The only way that storeMap
will trigger rerenders and effects is if you set it to a new Map every time you update it. If you absolutely must use Maps, then mutable refs are the closest thing to them that React offers. An Object is about the most complex thing that belongs in React state. See this thread.
Upvotes: 1
Reputation: 11156
Ciao, here working code. If you click key1
or key2
button, elements are added to the Map, if you click show result
button you will see storeMap
value.
Upvotes: 1