简体   繁体   中英

I am changing the state of the REACT component from another component but no refresh

I need a light to see what is missing?. I am changing the state of the Users REACT component when I add a user to the backend. The user added with no issues. And I send the state of the User element in and I change the state when "onSubmit:" happened but the Users REACT component didn't reload. What is wrong with this approach?

import { v4 as uuidv4 } from 'uuid';
import React, { useState, useEffect }  from 'react';
import { useFormik } from 'formik';
function unique_id() {
    return uuidv4()
}
var PlayersBackend='/players';
const validate = values => {
    const errors = {};
    if (values.firstName === 0 || values.firstName.replace(/^\s+|\s+$/gm, '').length === 0) {
        errors.firstName = 'Required';
    } else if (values.firstName.length === 0) {
        errors.firstName = 'The message must be no empty';
    }

    if (values.lastName === 0 || values.lastName.replace(/^\s+|\s+$/gm, '').length === 0) {
        errors.lastName = 'Required';
    } else if (values.lastName.length === 0) {

        errors.lastName = 'The message must be no empty';
    }
    return errors;
};
const addPlayer = async (firstName, lastName) => {
    await fetch(PlayersBackend, {
        method: 'POST',
        mode: 'cors',
        cache: 'no-cache',
        credentials: 'same-origin',
        headers: {
            "Content-Type":"application/json",
        },
        body: JSON.stringify({ firstName, lastName })
    })

};


const AddPlayerForm=(compUsersState) =>{
    const formik = useFormik(
        {initialValues:{firstName: '',lastName: '',},
            validate,
            onSubmit: (values,{resetForm}) =>
        {  addPlayer(values.firstName,values.lastName).then();
            resetForm();
            console.log(compUsersState.compUsersState)
            compUsersState.compUsersState(p => p+1);

        },
    });
    return (
        <form onSubmit={formik.handleSubmit}>
            <label htmlFor="firstName">First Name</label>
            <input
                id="firstName"
                name="firstName"
                type="text"
                onChange={formik.handleChange}
                value={formik.values.firstName}
            />
            <label htmlFor="lastName">Last Name</label>
            <input
                id="lastName"
                name="lastName"
                type="text"
                onChange={formik.handleChange}
                value={formik.values.lastName}
            />

            <button type="submit">Add user</button>
        </form>
    );
};

export const Users = () => {

    const [error, setError] = useState(null);
    const [isLoaded, setIsLoaded] = useState(false);
    const [users, setPlayers] = useState([]);
    const [,compUsersState] = useState(0);
    useEffect(() => {
        fetch(PlayersBackend).then(response => response.json()).then((data) => { setIsLoaded(true);setPlayers(data);}, (error) => {setIsLoaded(true);setError(error);})},[])
    if (error) {
        return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
        return <div>Loading...</div>;
    } else {

        if (typeof (users._embedded) !== 'undefined')
        {
            return (

                <ul>
                    <AddPlayerForm compUsersState={compUsersState}/>
                    {users._embedded.players.map(player => (
                        <li className="d-flex justify-content-start" key={unique_id()}>
                            {player.firstName} {player.lastName}
                        </li>
                    ))}

                </ul>
            );

        }
        else return  (<ul> </ul> )

    }

}

It might be due to useEffect it only executes once when the component is loaded adding users to useEffect dependency might help

 useEffect(() => {
    fetch(PlayersBackend)
      .then((response) => response.json())
      .then(
        (data) => {
          setIsLoaded(true);
          setPlayers(data);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, [users,users.length]);

Update: After seeing the answer about useEffect , which hints me a new idea to achieve the refreshing.

In Users ,

const [refreshCounter, compUsersState] = useState(0);


useEffect(() => {
    fetch(PlayersBackend)
      .then((response) => response.json())
      .then(
        (data) => {
          setIsLoaded(true);
          setPlayers(data);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, [refreshCounter]);

By adding the counter state into the dependency array, you can be sure that the useEffect is triggered every time the counter gets incremented.


I recommend passing setPlayers function to <AddPlayerForm> , and trigger the function to add a new player to the users list, after successfully added a player.

In Users ,

<AddPlayerForm compUsersState={compUsersState} setPlayers={setPlayers} />

In AddPlayerForm ,

const AddPlayerForm = ({compUsersState, setPlayers}) => {
  const formik = useFormik({
    initialValues: { firstName: "", lastName: "" },
    validate,
    onSubmit: (values, { resetForm }) => {
      addPlayer(values.firstName, values.lastName).then();
      resetForm();
      console.log(compUsersState.compUsersState);
      compUsersState.compUsersState((p) => p + 1);
      const newPlayer = values; // Or change the format
      setPlayers((p) => [...p, newPlayer])
    },
  });
  ...

I can see you are trying to refresh the Users component by calling compUsersState((p) => p + 1) , and I think it is working. If you are not sure, inspect the element with React Developer Tool and check the state has been successfully changed.

But the actual problem is that the users state didn't get updated. So the UI looks the same as before.

Try with forceupdate like

const forceUpdate = React.useReducer(bool => !bool)[1];

And call with state update

forceUpdate();

My final solution is using axios. I sent the state as a parameter
<AddPlayerForm useState={[listPlayers, setPlayers]}/>

and when I added a user I call

let temp_listPlayers=[...listPlayers,response.data]
setPlayers(temp_listPlayers);

This is the complete code for your reference.

import { v4 as uuidv4 } from 'uuid';
import React, { useState, useEffect }  from 'react';
import axios from 'axios';
import { useFormik } from 'formik';
// PlayersBackend must be change in Sync with the value in package.json 
// for now is set for the Backend and Frontend will be in the localhost 
//. "proxy": "http://localhost:8080", in package.json

var PlayersBackend='/players';
function unique_id() {
    return uuidv4()
}

const validate = values => {
    const errors = {};
    if (values.firstName === 0 || values.firstName.replace(/^\s+|\s+$/gm, '').length === 0) {
        errors.firstName = 'Required';
    } else if (values.firstName.length === 0) {
        errors.firstName = 'The message must be no empty';
    }
    if (values.lastName === 0 || values.lastName.replace(/^\s+|\s+$/gm, '').length === 0) {
        errors.lastName = 'Required';
    } else if (values.lastName.length === 0) {
        errors.lastName = 'The message must be no empty';
    }
    return errors;
};
function AddPlayer(firstName,lastName,useState) {
    const data = { firstName: firstName, lastName: lastName };
    let listPlayers=useState.useState[0];
    let setPlayers=useState.useState[1];
    axios.post(PlayersBackend, data)
        .then(response => {
            let temp_listPlayers=[...listPlayers,response.data]
            setPlayers(temp_listPlayers);
         }).catch(error => {
        console.error('Something went wrong!', error);
    });
;}

const AddPlayerForm=(useState) =>{
     const formik = useFormik(
        {initialValues:{firstName: '',lastName: '',},
            validate,
            onSubmit: (values,{resetForm}) =>
        {  AddPlayer(values.firstName,values.lastName,useState);
           resetForm();
        },
    });
    return (
        <form onSubmit={formik.handleSubmit}>
            <label htmlFor="firstName">First Name</label>
            <input
                id="firstName"
                name="firstName"
                type="text"
                onChange={formik.handleChange}
                value={formik.values.firstName}
            />
            <label htmlFor="lastName">Last Name</label>
            <input
                id="lastName"
                name="lastName"
                type="text"
                onChange={formik.handleChange}
                value={formik.values.lastName}
            />

            <button type="submit">Add user</button>
        </form>
    );
};

export const Users = () => {
   const [listPlayers, setPlayers] = useState({});
   const getPlayers = () =>
       axios.get(PlayersBackend)
           .then((response)=> {
               const myPlayers=response.data._embedded.players;
               setPlayers(myPlayers);
           })
        useEffect(()=>getPlayers(),[])
    if (typeof(listPlayers.length) !== 'undefined') {
         return (
            <ul>
                <AddPlayerForm useState={[listPlayers, setPlayers]}/>
                {listPlayers.map(player => (
                <li className="d-flex justify-content-start" key={unique_id()}>
                    {player.firstName} {player.lastName}
                </li>
                ))}
            </ul>
        );}
    return (
        <ul> </ul>
    );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM