0

I have a catalogue type program for a school project - it pulls in a few arrays and concatenates the arrays with arrays created from user input. The only issue is that they state is always one behind so if you increase the counter to 5 and submit it will return 4 instead. Any ideas?

const Booking = ({currentId}) => {
    const dispatch = useDispatch();
    
    const user = JSON.parse(localStorage.getItem('profile'));
    const users = useSelector((state) => state.users);
    const filteredRentals = users.filter((users)=> users._id === user?.result?._id);

    const rentedArr = filteredRentals[0].rentedProducts;
    const numbArr = filteredRentals[0].numberOfProducts;

    const [userRental, setUserRental] = useState({rentedProducts:'', numberOfProducts:0});
    const [newRental, setNewRental] = useState({rentedProducts:rentedArr, numberOfProducts:numbArr});

    const userID = user?.result?._id;
    const newRented = rentedArr.concat(userRental.rentedProducts);
    const newNumb = numbArr.concat(userRental.numberOfProducts);

    const handleSubmit = (e) => {
        e.preventDefault();

        if(currentId){
            dispatch(updateRentals(userID, newRental));
        } else {
            console.log('No product found');
        } clear();
    }

    const clear = () =>{
        setUserRental({rentedProducts:'', numberOfProducts:0})
    }

    const changeHandler = (e) => {
        setUserRental({...userRental, numberOfProducts: e.target.value, rentedProducts: product.title});
        setNewRental({...newRental, numberOfProducts: newNumb, rentedProducts: newRented});
    }
    return(
        <div className='booking-panel-wrap'>
            <h3>Book an Item</h3>
            <form onSubmit={handleSubmit}>
                <label htmlFor="inStock">Units Required:
                    <input type="number" required min="0" name="inStock" onChange={(e) => changeHandler(e)}/>
                </label>
                <button type="submit" className='btn-white'>Submit</button>
            </form>
        </div>
    )
    
}

export default Booking;
Destinneh
  • 33
  • 5

1 Answers1

0

newNumb and newRented within your changeHandler are both dependent on userRental. setUserRental updates userRental on the next render, the current userRental does not change after calling setUserRental. Therefore the values of newNumb and newRented are incorrect (stale) and should be re-evaluated within the changeHandler.

const changeHandler = (e) => {
  // store the new `userRental` in a variable, before passing it to `setUserRental()`
  const newUserRental = {...userRental, numberOfProducts: e.target.value, rentedProducts: product.title};
  setUserRental(newUserRental);

  // build `newRental` based on the new value of `userRental` (stored in the `newUserRental` variable)
  const newNumb = numbArr.concat(newUserRental.numberOfProducts);
  const newRented = rentedArr.concat(newUserRental.rentedProducts);
  setNewRental({...newRental, numberOfProducts: newNumb, rentedProducts: newRented});
};

The above is probably more complicated than it needs to be. The state newRental can probably be removed (or changed into a useMemo), which simplifies things quite a lot.

In the example below I removed the newRental state completely and build the object as part of the submit action.

const Booking = ({currentId}) => {
    const dispatch = useDispatch();
    
    const user = JSON.parse(localStorage.getItem('profile'));
    const userID = user?.result?._id;
    const users = useSelector((state) => state.users);
    // If you only need the first match of a `filter()` result, use `find()` instead.
    // `filteredRental` is only used inside `handleSubmit()` so should probably be moved there.
    const filteredRental = users.find((user) => user._id === userID);

    const [userRental, setUserRental] = useState({ rentedProducts: '', numberOfProducts: 0 });

    const handleSubmit = (e) => {
        e.preventDefault();

        if (currentId) {
            // don't store the newRental in a state, but build it during submit
            const newRental = {
                rentedProducts: filteredRental.rentedProducts.concat(userRental.rentedProducts),
                numberOfProducts: filteredRental.numberOfProducts.concat(userRental.numberOfProducts);
            };
            dispatch(updateRentals(userID, newRental));
        } else {
            console.log('No product found');
        } clear();
    }

    const clear = () =>{
        setUserRental({rentedProducts:'', numberOfProducts:0})
    }

    const changeHandler = (e) => {
        setUserRental({...userRental, numberOfProducts: e.target.value, rentedProducts: product.title});
    }
    
    // ...
}

export default Booking;
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52