I'm trying to add a key-value pair to an object using useState
inside map
function. However, the spread operation in setContainer({...container, [data.pk]: data.name});
seems to be ineffective.
The code snippet below exactly illustrates the problem that I'm trying to solve. In fact, in my original code container
is declared at another ContextProvider component and referenced using useContext
, but I replaced it with useState
for simpliticy.
I guess it has to do with the asynchronous nature of setState
hook, but I don't fully understand what's going on under the hood.
Please let me know what is causing this issue and how to yield the outcome I expected.
Thank you.
const {useState, useEffect} = React;
const myData = [{pk: 1, name:'apple'},
{pk: 2, name:'banana'},
{pk: 3, name:'citrus'},
]
const Example = () => {
const [container, setContainer] = useState({});
useEffect(()=>{
myData.map(data=>{
console.log(`During this iteration, a key-value pair (key ${data.pk} and value '${data.name}') should be added to container`);
setContainer({...container, [data.pk]: data.name});
})
}, [])
return (
<div>
<div>result I expected: {{'1': 'apple', '2': 'banana','3': 'citrus'}}</div>
<div>result: {JSON.stringify(container)}</div>
</div>
);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById(react)
);
<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=react></div>
Update
The main reason I wanted to use useState
in loops was to use the state as a container of refs
of components. However, I found it inefficient to do so, because it caused too many rerenders and was hard to determine when the loop finished. After a lot of searching, I ended up declaring useRef({})
in the Context Provider and use it as a refs container (which seems to fit the actual purpose it was created I guess). The refs container can be used as following:
const refsContainer = useRef({});
import React from 'react';
const Notes = () => {
const setRef = (noteId) => (element) => {
refsContainer.current[noteId] = element
};
return (
<Masonry>
{pubData?.map(publication => {
return <PublicationCard innerRef={setRef(publication.noteId)}/>
})}
</Masonry>
);
};
export default Notes;
const pubCardRef = refsContainer.current[rank.note.noteId];
window.scrollTo({top: pubCardRef?.offsetTop - 80, behavior: 'smooth'})