javascriptreactjslaravelfsc

React Stateless Functional Components - Laravel


I setup a project with laravel as backend Api provider and React as Frontend manager. React stateless functional component work well when I simply render some component.

const [state, setState] = useState({data:someData})

I use useEffect to fetch some data with Axios from an API URL. fine

But when I change the state of a component with setState, it does not work. No change is reflected. it looks like a static page even if I hit 100 times the button which has the click event. the console is clear. No errors

I want to know why? and how can I fix it.

Note: Class based component does not have this problem. it perfectly renders any update of the state

Some codes

const Users = (props) => {

const [state, setState] = useState({

    dataPraticiens: {
        data: [],
        currentPage: 1,
        PerPage: 3
    },
    loaded: false

});


useEffect(() => {
    Axios.get('/api/users').then((r) => {

        setState({

            dataPraticiens: {
                data: [...r.data.docs],
                currentPage: 1,
                PerPage: 3
            } ,

            loaded: true
        });

    }).catch((er)=> {
        console.log(er);
    })

},[state.loaded]);

const changePage = (event) => {

    let nS = state;
    nS.dataPraticiens.currentPage = Number(event.target.id);
    nS.loaded = false;
    setState(nS); 
    // log the state and yeah, state changes but not reflected in the page
}


const indexOfLast = state.dataPraticiens.currentPage * state.dataPraticiens.PerPage;

const indexOfFirst = indexOfLast - state.dataPraticiens.PerPage;

const current = state.dataPraticiens.data.slice(indexOfFirst, indexOfLast);


const pageN = [];
for (let i = 1; i <= Math.ceil(state.dataPraticiens.data.length / state.dataPraticiens.PerPage); i++) {
    pageN.push(i);
}

const renderPageN = pageN.map(num => {
    return (
        <li
            key={num}
            id={num}
            onClick={changePage} // Not work here.
        >
            {num}
        </li>
    );
});
return (
        {current.map((value, index) => {
           return <React.Fragment key={index}>
                     <li>{value}</li>
                  </React.Fragment>
         })}
        <ul>
          {renderPageN} // pages number`
        </ul>

     )}

Solution

  • In useEffect you should use an empty array [] to indicate that you want the effect to execute only once. Using [state.loaded] will reset the state on every state update, since the state update will cause change in state.loaded and this change will trigger the effect hook.

    Another thing is that you should not mutate the state directly here:

    let nS = state;
    nS.dataPraticiens.currentPage = Number(event.target.id);
    nS.loaded = false;
    setState(nS);
    

    Note that nS is just a reference to the same state object, so you technically modifying the state directly which can lead to an undefined behaviors and odd bugs. You should build a new copy of the state object:

    const newState = {
      dataPraticiens: {
        ...state.dataPraticiens,
        currentPage: Number(event.target.id)
      }, 
      loaded: false
    };
    
    setState(newState);
    

    From the React docs:

    NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.