更新状态时,useEffect 钩子不会重新呈现

useEffect hook doesn't rerender when state is updated

我正在使用 Tailwind 和 Redux 构建一个 React 日历,但我遇到了一个小问题。我有一个表格,我可以在其中创建一个新事件,并将这个日期推送到我的状态 'savedEvents'。因此,我想使用我的状态中的事件动态地生成一些跨度,但是当我的状态更新时我的 useEffect 挂钩无法重新呈现,我不明白为什么 .

GitHub 代码存储库:https://github.com/snnack123/Events-Calendar

我的全局状态(/store/events.js):

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;
    }
}

我的提交表单 (/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>

从这个表单更新我的状态的函数:

    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();
    }

这是我的 useEffect 不起作用 (/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]);

我想在这里使用我的状态:

                {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>
                ))}
...state.savedEvents.push(action.payload)

你正在改变你的数组,所以它是同一个数组并且不会触发效果。改为创建一个新的。

savedEvents: [...state.savedEvents, action.payload]

钩子UseEffect工作正常,问题的根源是:

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;
    }
}

你的组件不会 re-render 甚至状态的变化,因为你改变了数组 savedEvents 并且 Redux 不知道数组有变化 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, savedEvents: [...state.savedEvents, action.payload] }; // <== add this to trigger the re-rendering of your component
      default:
          return state;
  }
}