0

I have a problem with two functions the problem is should click twice on the button to make a change and should click one time only

const addToBalance = (personId) => {
    userInfo.map((user) => (
        user._id === personId ? setUserGetMoney(user.balance) : ''
    ))
    if (userGetMoney !== 0 && parseInt(inputMoney) !== 0 && parseInt(inputMoney) !== null){
        setUserGetMoney(userGetMoney + parseInt(inputMoney))
        console.log(userGetMoney)
    }
}
const removeFromBalance = () => {
    const id = params.id;
    userInfo && userInfo.map((user) => (
        user._id === id ?  setUserRemoveMoney(user.balance) : ''
    ))
    if (userRemoveMoney !== 0 && personId && parseInt(inputMoney) !== 0 && parseInt(inputMoney) !== null){
       setUserRemoveMoney(userRemoveMoney - parseInt(inputMoney))
       console.log(userRemoveMoney)
    }
} 

submit function

const onSubmitForm = (e) => {
    e.preventDefault();
    addToBalance(personId);
    removeFromBalance();
    if (personId && parseInt(inputMoney) !== 0 && parseInt(inputMoney) !== null){
        postTransactionData();
    }
}
<button type='submit' onClick={(e) => onSubmitForm(e)}>Submit</button>

When I try to click the submit button to execute the two functions addToBalance and removeFromBalance they don't work but when I click again on the submit button it works I want a solution so that I only click once on the button to make the functions work

the video explains the problem: https://drive.google.com/drive/folders/18usf6CXOyaDi4Py_VbEeNYdyE5Sl91-T?usp=sharing

Ahmed Radi
  • 677
  • 1
  • 5
  • 20

1 Answers1

1

Your issue is that you are setting state and expecting that state change to be a: synchronous, and b: available in that render.

There are quite a few resources that get into exactly what's going on, but the important thing to note is that the values within your functions are inside what's known as a closure, and they won't change... ever, they only appear to change because re-rendering re-creates the variables and those will have the new values, but they are by definition different variables each render.

How To Solve The React Hook Closure Issue?

It fails the first time because your userGetMoney and userRemoveMoney variables are likely set to 0 to start, then the first button click causes them to be set to the current balance. The second button click causes them to be set with the current balance, and then set again with the current balance +/- the inputMoney. And if you change users after the first run, you'd see that something really crazy occurs - it would be setting the new user's money based on what the previous one had.

My recommendation to you is to just use the values you have and pass it around within the function instead of using React State for this.

const { useState } = React;

function App({ userId }) {
  const [inputMoney, setInputMoney] = useState("");
  const [userInfo, setUserInfo] = useState(() => [
    { _id: 1, balance: 200, name: "bob" },
    { _id: 2, balance: 250, name: "joe" },
    { _id: 3, balance: 300, name: "billy" },
  ]);
  const [sendeeId, setSendeeId] = useState('');

  function postTransactionData({ sender, sendee, money }) {
    const updatedSender = { ...sender, balance: sender.balance - money };
    const updatedSendee = { ...sendee, balance: sendee.balance + money };
    if(updatedSender.balance<0){
      // Don't have enought funds!
      return;
    }
    console.log("transaction:", updatedSender, updatedSendee);
    setUserInfo((prev) =>
      prev.map((user) => {
        if (user._id === sender._id) {
          return updatedSender;
        }
        if (user._id === sendee._id) {
          return updatedSendee;
        }
        return user;
      })
    );
  }

  function getUser(personId) {
    return userInfo.find((user) => user._id === personId);
  }

  const onSubmitForm = (e) => {
    e.preventDefault();
    const sender = getUser(userId);
    const sendee = getUser(sendeeId);
    const money = parseInt(inputMoney,10);
    if (!sender || !sendee || !money || Number.isNaN(money)) {
      //Invalid transaction
      return;
    }
    postTransactionData({ sender, sendee, money });
  };
  return (
    <form onSubmit={onSubmitForm}>
      <input
        name="money"
        type="number"
        value={inputMoney}
        onChange={(e) => setInputMoney(e.target.value)}
      />
      <select value={sendeeId} onChange={(e) => setSendeeId(+e.target.value)}>
        <option value={''}>Unset</option>
        {userInfo.filter(({_id})=>_id!==userId).map((user) => (
          <option key={user._id} value={user._id}>{user.name}</option>
        ))}
      </select>
      <button type="submit">Submit</button>
    </form>
  );
}

ReactDOM.render(<App userId={1} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"/>
Zachary Haber
  • 10,376
  • 1
  • 17
  • 31
  • I appreciate the information and advice you have shared. Thank you very much. I have one question why use `+` in the `setSendeeId` here ` – Ahmed Radi Mar 13 '22 at 13:29
  • That's a unary plus operator, which converts the value to a number. – Zachary Haber Mar 13 '22 at 15:01
  • Thank you, bro, I am very happy with your answer, I think in my code no need to `+` because I am using `ID` as `String` thank you again <3 – Ahmed Radi Mar 13 '22 at 22:30