更新状态时,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;
}
}
我正在使用 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;
}
}