Reputation: 37
I'm trying to build a chat application, and I have a function for selecting users you want to add to the chat. It is in the following component (I've left out the import statements and one of the functions for brevity):
const AddUserBox = (props) => {
// context:
const { friends, getFriends, user, whoToMsg } = useContext(AppContext);
// state:
const [friendsDisplay, setFriendsDisplay] = useState();
const [selected, setSelected] = useState();
const getFriendsDisplay = async function() {
if (!friends) await getFriends(user.username);
if (friends) {
let currentFriend;
let result;
let friendsInfo = [];
friends.sort();
for (let i = 0; i < friends.length; i++) {
if (friends[i].user_1 === user.username) {
currentFriend = friends[i].user_2;
} else {
currentFriend = friends[i].user_1;
}
if (currentFriend === whoToMsg) {
return;
} else if (typeof whoToMsg === Array) {
if (whoToMsg.includes(currentFriend)) return;
}
await fetch('/proxy/get-user-update', {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type':'application/json'
},
body: JSON.stringify({username: currentFriend})
})
.then(results => results.json())
.then(data => result = data)
.catch(err => console.error(err));
friendsInfo.push(result);
}
setFriendsDisplay(friends.map((friend, i) => {
return <div
className="add-user-friends-button"
key={i}
i={i}
onClick={(e) => {selectUser(e, friend)}}
>
{friendsInfo[i].name}
</div>
}));
}
}
const selectUser = function(e, friend) {
let newArr = [];
let currentFriend;
if (friend.user_1 === user.username) {
currentFriend = friend.user_2;
} else {
currentFriend = friend.user_1;
}
if (selected) {
newArr = [...selected];
if (selected.includes(currentFriend)) {
e.target.style.backgroundColor = "";
newArr.splice(newArr.indexOf(currentFriend), 1);
setSelected(newArr);
return;
}
}
newArr.push(currentFriend);
newArr.sort();
e.target.style.backgroundColor = "#E6FCFF";
setSelected(newArr);
console.log(selected);
}
return (
<div className="add-user">
<span>Add users to chat:</span>
<div className="add-user-friends">
{friendsDisplay}
</div>
<button onClick={() => props.close()}>Close</button>
<button onClick={() => addUsers()}>Add users</button>
</div>
);
}
The idea is that you select a user from a list and it is added to an array of selected users so that when they click the add users button the users are added to the chat.For some reason setSelected(newArr)
does not set the state to the new array. Can anyone help me understand / fix this?
Upvotes: 2
Views: 4830
Reputation: 4557
The main thing awry here is that the value of selected
will not be updated immediately after calling setSelected()
(see post link to thread below).
Instead of trying to access the most recent state within a callback, use useEffect
. Setting your state with the function returned from setState
will not immediately update your value. The state updates are batched and updated on the next render.
It may help if you think of useEffect()
like setState
's second parameter (from class based components).
If you want to do an operation with the most recent state, use useEffect()
which will be hit when the state changes:
const {
useState,
useEffect
} = React;
function App() {
const [count, setCount] = useState(0);
const decrement = () => setCount(count-1);
const increment = () => setCount(count+1);
useEffect(() => {
console.log("useEffect", count);
}, [count]);
console.log("render", count);
return (
<div className="App">
<p>{count}</p>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( < App / > , rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Some further info on useEffect()
Upvotes: 1
Reputation: 11
Since setState is an async method, you don't always get the expected value when you call it right away. You can pass a callback method to your setState. If you move your console.log statement into there, you should see your expected results.
There's some information here: https://dev.to/dance2die/accessing-react-state-right-after-setting-it-2kc8
Upvotes: 1