react: сохранение состояний при функциональном программировании
ситуация следующая (как я ее понимаю):
при определении функционального компонента (чтоб не захламлять специально его максимально вычистил)
export interface IProps {
title : string,
timer : number
}
const Component = (props: IProps ) => {
// контроль состояний
const [states, setCountdownParams] = useState<IStates>({
a: props.timer,
b: true
});
return <></>
}
при передаче новых параметров компоненту
<Component title="t1" timer=10 /> => <Component title="t2" timer=10 />
происходит установка заново состояний, т.е.
const [states, setCountdownParams] = useState<IStates>({
a: props.timer,
b: true
});
как этого избежать?
мне хотелось бы, чтоб компонент использовал старые значения, т.е. состояния не обновлялись бы.
Вариант, который приходит в голову - добавить колбэк в класс, который будет возвращать актуальное состояние, таким образом всегда можно при новом вызове компонента использовать старое состояние - но как по мне - это жуткий костыль
А как это сделать правильно?
Объясню ситуацию на прикладной задаче - у меня есть компонент с обратным отсчётом, когда обратный отсчёт заканчивается, то он должен продолжать отобраться корректно. Остановка обратного отсчёта сделана через props, в результате начинает отобраться начальное время (из-за причин описанных выше):
interfaces:
import {CSSProperties} from 'react';
// интерфейс для пропсов
export interface IPropsTaskHeader {
title : string, // название задачи
deadline : number, // время выполнения задачи (в секундах)
active? : boolean, // активна ли задача
onStop? : any // колбэк на завершение задачи
}
// интерфейс для состояний
export interface IStatesTaskHeader {
timer_state : number, // идентификатор состояния таймера времени выполнения задачи
timer_global : number, // текущее глобальное значение таймера время выполнения задачи (в миллисекундах)
timer_local : number // текущее локальное значение таймера время выполнения задачи (в миллисекундах)
}
// интерфейс для стилей
export interface IDataCountdown {
[key: number]: {
deadline : number, // время срабатывания следующего таймера
style : CSSProperties // стили для счётчика обратного отсчёта
};
}
component:
// компонент заголовка задачи (с элементами управления)
const TaskHeader = (props: IPropsTaskHeader) => {
// контроль состояний
const [states, setCountdownParams] = useState<IStatesTaskHeader>({
timer_state : 0,
timer_global : Date.now() + 1000 * props.deadline,
timer_local : 1000 * props.deadline
});
// стили для разных состояний
const styles: IDataCountdown = {
0: {deadline: 60, style: {color: "#000000", textAlign: "center"}},
1: {deadline: 10, style: {color: "#bb0000", textAlign: "center"}},
2: {deadline: 0, style: {color: "#bb0000", textAlign: "center", animation: "neon 1s infinite"}}
}
// колбэк: изменение времени таймера
const handleChangeTimer = (timer: any) => {
const t: number = 1000 * styles[states.timer_state]?.deadline;
if (timer <= t) {
setCountdownParams({
timer_state : states.timer_state + 1,
timer_global : Date.now() + t,
timer_local : t
});
}
}
// колбэк: завершение работы таймера
const handleFinishTimer = () => {
setCountdownParams({
timer_state : states.timer_state,
timer_global : Date.now(),
timer_local : 0
});
props.onStop?.();
}
// отрисовать компонент
return (
<div className="task-header">
<div className="task-header-title">{props.title}</div>
{
props.active ?
<Statistic.Countdown title="Время выполнения" value={states.timer_global} format="mm:ss" valueStyle={styles[states.timer_state].style} onChange={handleChangeTimer} onFinish={handleFinishTimer} className="task-header-statistics" /> :
<Statistic title="Время выполнения" value={new Date(states.timer_local).toISOString().slice(-10, -5)} valueStyle={styles[states.timer_state].style} className="task-header-statistics" />
}
</div>
);
}