Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

useEffect hook doesn't rerender when state is updated

I’m buildind a React Calendar using Tailwind and Redux and I have a little problem. I have a form where I create a new event and I push this date in my state ‘savedEvents’. Atfer that, I want to use events from my state to generate dinamically some spans but my useEffect hook can’t rerender when my state is updated and I don’t understand why.

GitHub repository for code: https://github.com/snnack123/Events-Calendar

My global state (/store/events.js):

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import dayjs from 'dayjs'

const initialState = {
    monthIndex: dayjs().month(),
    compare: dayjs().month(),
    smallCalendarMonth: 0,
    daySelected: dayjs(),
    showEventModal: false,
    savedEvents: [],
}

export function eventsReducer(state = initialState, action) {
    switch (action.type) {
        case 'events/setMonthIndex':
            return { ...state, monthIndex: action.payload };
        case 'events/setCompare':
            return { ...state, compare: action.payload };
        case 'events/setDaySelected':
            return { ...state, daySelected: action.payload };
        case 'events/setShowModal':
            return { ...state, showEventModal: action.payload };
        case 'events/setNewEvent':
            return { ...state, ...state.savedEvents.push(action.payload) };
        default:
            return state;
    }
}

My submit form (/components/EventModal.js):

<form className='bg-white rounded-lg shadow-2xl w-1/4'>
                <header className='bg-gray-100 px-4 py-2 flex justify-between items-center'>
                    <span className='material-icons-outlined text-gray-400'>
                        drag_handle
                    </span>
                    <button onClick={setShowModal}>
                        <span className='material-icons-outlined text-gray-400'>
                            close
                        </span>
                    </button>
                </header>
                <div className='p-3'>
                    <div className="grid grid-cols-1/5 items-end gap-y-7">
                        <div></div>
                        <input
                            type="text"
                            name="title"
                            placeholder='Add title'
                            value={title}
                            onChange={(e) => setTitle(e.target.value)}
                            required
                            className='pt-3 border-0 text-gray-600 text-xl font-semibold pb-2 w-full border-b-2 
                            border-gray-200 focus:outline-none focus:ring-0 focus-blue-500'
                        />
                        <span className='material-icons-outlined text-gray-400'>
                            schedule
                        </span>
                        <p>{daySelected.format('dddd, MMMM DD')}</p>
                        <span className='material-icons-outlined text-gray-400'>
                            segment
                        </span>
                        <input
                            type="text"
                            name="description"
                            placeholder='Add a description'
                            value={description}
                            onChange={(e) => setDescription(e.target.value)}
                            required
                            className='pt-3 border-0 text-gray-600 pb-2 w-full border-b-2 
                            border-gray-200 focus:outline-none focus:ring-0 focus-blue-500'
                        />
                        <span className='material-icons-outlined text-gray-400'>
                            bookmark_border
                        </span>
                        <div className="flex gap-x-2">
                            {labelsClasses.map((lblClass, i) => (
                                <span key={i} className={`bg-${lblClass}-500 w-6 h-6 rounded-full flex items-center justify-center cursor-pointer`}
                                    onClick={() => setSelectedLabel(lblClass)}>
                                    {selectedLabel === lblClass && (
                                        <span className="material-icons-outlined text-white text-sm">
                                            check
                                        </span>
                                    )}
                                </span>
                            ))}
                        </div>
                    </div>
                </div>
                <footer className='flex justify-end border-t p-3 mt-5 '>
                    <button type='button' className='bg-blue-500 hover:bg-blue-600 px-6 py-2 rounded text-white' onClick={saveEvent}>
                        Save
                    </button>
                </footer>
            </form>

Function which update my state from this form:

    const [title, setTitle] = useState('');
    const [description, setDescription] = useState('');

    const labelsClasses = [
        "indigo",
        "gray",
        "green",
        "blue",
        "red",
        "purple",
    ];

    const [selectedLabel, setSelectedLabel] = useState(labelsClasses[0]);

    function saveEvent() {
        let new_event = {
            title,
            description,
            label: selectedLabel,
            day: daySelected.valueOf(),
            id: Date.now(),
        };

        dispatch({ type: 'events/setNewEvent', payload: new_event });
        setShowModal();
    }

This is my useEffect that doesn’t work (/components/Day.js)

    const savedEvents = useSelector((state) => state.events.savedEvents);
    const [dayEvents, setDayEvents] = useState([]);

    useEffect(() => {
        const events = savedEvents.filter(
            (evt) =>
                dayjs(evt.day).format("DD-MM-YY") === day.format("DD-MM-YY")
        );
        setDayEvents(events);
    }, [savedEvents, day]);

I want to use my state here:

                {dayEvents.map((evt, idx) => (
                    <div
                        key={idx}
                        className={`bg-${evt.label}-200 p-1 mr-3 text-gray-600 text-sm rounded mb-1 truncate`}
                    >
                        {evt.title}
                    </div>
                ))}

>Solution :

The hook UseEffect is working fine, the source of the problem is this:

export function eventsReducer(state = initialState, action) {
    switch (action.type) {
        case 'events/setMonthIndex':
            return { ...state, monthIndex: action.payload };
        case 'events/setCompare':
            return { ...state, compare: action.payload };
        case 'events/setDaySelected':
            return { ...state, daySelected: action.payload };
        case 'events/setShowModal':
            return { ...state, showEventModal: action.payload };
        case 'events/setNewEvent':
            return { ...state, ...state.savedEvents.push(action.payload) }; // <== This will not trigger the re-rendering of your component
         default:
            return state;
    }
}

Your component will not re-render even with the state changes.

Solution:

export function eventsReducer(state = initialState, action) {
  switch (action.type) {
      case 'events/setMonthIndex':
          return { ...state, monthIndex: action.payload };
      case 'events/setCompare':
          return { ...state, compare: action.payload };
      case 'events/setDaySelected':
          return { ...state, daySelected: action.payload };
      case 'events/setShowModal':
          return { ...state, showEventModal: action.payload };
      case 'events/setNewEvent':
          return { ...state, savedEvents: [...state.savedEvents, action.payload] }; // <== add this to trigger the re-rendering of your component
      default:
          return state;
  }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading