2

I'm pretty new to coding so I hope my question won't sound too ridiculous. I've spent days to figure something out but didn't find anything. So I have a react page with the main component that is my "planning container". In this component, I have a child component that represents a whole month. it's a table with a line for each people and my team, and a td that represents each day of the month. This child is stored in a variable array that is stored in the state. When I click on a button, I create another child (for the next month), that I will push into the array stored in the state.

I have a variable called "refresh" that just setRefresh(!refresh). This variable is passed to the childs components and I put this variable in the useEffect [] that will trigger a re-render. => The problem is that it doesn't re-render at all.

So, just for you to know, I really need to be able to "append" a whole month at each click on the button. I really need this "view" to be able to work. I will paste some code to make you understand the main idea.

here is the parent :

    import React, {useEffect, useState} from "react";
    import PlanningMonthBuild from "./PlanningMonthBuild";
    import './Planning.css';
    import "react-datepicker/dist/react-datepicker.css";
    
    const PlanningBuild = () => {
        const [startForNextMonth, setStartForNextMonth] = useState(new Date());
        const [inputList, setInputList] = useState([]);
        const [refresh, setRefresh] = useState(false);
    
        useEffect(() => {
            initPlanning();
        }, []);
    
        const handleRefresh = () => {
            setRefresh(!refresh);
        }
    
        const initPlanning = () => {
            let date = new Date(startForNextMonth.getFullYear(), startForNextMonth.getMonth(), 1);
            let newPlanning = [...inputList];
            newPlanning.splice(0, 0,
                <PlanningMonthBuild
                    key={'current' + new Date().toISOString()}
                    startDate={date}
                    refresh={refresh}
                />
            );
            let date2 = new Date(startForNextMonth.getFullYear(), startForNextMonth.getMonth() + 1, 1);
            date2.setMonth(date.getMonth() + 1);
            setStartForNextMonth(date2);
            setInputList(newPlanning);
        };
    
        const addOneMonthNext = () => {
            setInputList(inputList.concat(
                 <PlanningMonthBuild
                      key={new Date().toISOString()}
                      startDate={startForNextMonth}
                      refresh={refresh}
                 />
            ));
            let date = startForNextMonth;
            date.setDate(1);
            date.setMonth(date.getMonth() + 1);
            setStartForNextMonth(date);
        };
    
        return (
            <main>
                <div className="refresh-div" onClick={handleRefresh}><i className="fa fa-refresh" aria-hidden="true"></i></div>
                <button className="myButton" onClick={addOneMonthNext}>Add Next Month</button>
                <div id="insideDiv">
                    {inputList}
                </div>
            </main>
        );
    };
    
    export default PlanningBuild;

And here is the child (that build each month) :

    import React, {useEffect, useState} from "react";
    import useAxiosPrivate from "../hooks/useAxiosPrivate";
    import {getFirstDayOfMonth} from "../utils/dates";
    
    const PlanningMonthBuild = ({ startDate, refresh }) => {
        const [dateDebut] = useState(startDate);
        const [jsListe, setJsListe] = useState([]);
        const axiosPrivate = useAxiosPrivate();
    
        useEffect(() => {
            let isMounted = true;
            const controller = new AbortController();
    
            try {
                axiosPrivate.get('api/planning/team?start=' + getFirstDayOfMonth(dateDebut.getFullYear(), dateDebut.getMonth()), {
                    signal: controller.signal
                }).then(response => {
                    if (isMounted) {
                        const newListeJS = response.data;
                        setJsListe(newListeJS);
                    }
    
                });
    
            } catch (err) {}

            return () => {
                isMounted = false;
                controller.abort();
            }
    
        }, [refresh])
    
        return (jsListe.map((day)=> {
            ///.... building a table(tr,td...)
        }))
    
    }
    
    export default PlanningMonthBuild;

So, when I put my child component directly into the parent return, it works when I click on refresh button (if my workmate updates something for example...), it will update my table but if I store my child component in the array in "inputList" state it doesn't...

I hope you can understand what I mean.

Thanks in advance for any help.

yohanes
  • 2,365
  • 1
  • 15
  • 24
BadBadDev
  • 21
  • 2
  • 2
    Don't put components into state. Put plain data (arrays/objects/primitives) into state, then render it into components only when rendering. – CertainPerformance Jun 27 '22 at 02:46
  • @CertainPerformance I'm not sure to understand how to do. Do you have an example of code that would show how to do that? – BadBadDev Jun 27 '22 at 02:58
  • I'm not exactly sure what you're trying to do but I would fetch each month that you want to render and store it in state as an Array of Objects, [{month 1 information}, {month 2 information}] etc. Then I'd map through that state, passing the information for each month into the child component. – Benjamin Bialy Jun 27 '22 at 03:36
  • @BenjaminBialy I can understand but the problem is that my children component are pretty "heavy" and it's pretty long to render. I mean, If I render only one at a time it's ok, but if I do like you say, it will rerender each children every time I had one...Or maybe is there another way to do? I'd like to be able to have [month1] [month2] already rendered for example, and to add month3 without rerendering month1 and month2. That's what my code do efficiently, but I loose the ability to update this children components. – BadBadDev Jun 27 '22 at 05:29
  • You should use the useMemo hook, check out this answer https://stackoverflow.com/questions/70492138/is-it-possible-to-add-item-to-a-react-array-state-without-re-rendering-all-items for how to implement. – Benjamin Bialy Jun 27 '22 at 09:23

0 Answers0