У меня есть функциональный компонент React, который рендерит JSX, содержащий абзац, раскрывающийся список из библиотеки Select React JS и линейный график из библиотеки Nivo.
При первом отображении компонента У меня есть useEffect
ловушка, которая помогает мне получать данные, список, из бэкэнда. Данные возвращаются из бэкэнда на основе значения mode .
Этот mode может быть:
, и у меня для него есть useState const [mode, setMode] = useState("week")
.
В раскрывающемся списке Select есть прослушиватель событий onChange
, который помогает мне установить новый режим
setMode(e.value)
- может быть неделя , месяц или год
Когда я устанавливаю новый режим , он возвращается к бэкэнду и получает новый список с данными.
Моя проблема заключается в следующем:
- Я выбираю новое значение из раскрывающегося списка
- Мой компонент повторно отображает и получает текущее значение моего массива
- Затем он меняет режим на новое значение
- Затем
useEffect
получает новые данные на основе нового режима и повторного рендеринга с новым списком.
Например:
Мое текущее значение в режиме week . Данные поступают из бэкэнда и корректно отображаются на графике. Я выбираю новый режим месяц и звоню setMode("month")
. Первые вещи, которые происходят:
- Компонент перерисовывает
- Получает текущие данные для диаграммы, которые составляют неделя
useEffect
запрос GET для нового режима month data и устанавливает новые данные на графике - Если текущий режим month и я хочу показать диаграмму с год . Снова в раскрывающемся списке
onChange
Я установил режим на год , но первый компонент повторно отображает текущее значение массива, равное месяц , а затем возвращает данные для год .
Я не хочу получать текущее значение массива первым при каждом выборе нового значения из падать. Я только хочу установить Mode с новым значением и получить данные с сервера с новым списком.
Вот мой код:
React Component:
import { useFeedbackDistribution } from "../../api/analytics.firebase";
import { DISTRIBUTION_MODE } from "../../utils/query_filters";
// UI
import Loading from "../App/loadingIndicator";
import { ErrorSolid } from "../Errors/error.solid";
import { ResponsiveLine } from "@nivo/line";
import Select from "react-select";
import {
dayTimeScaleProperties,
monthTimeScaleProperties,
yearTimeScaleProperties
} from "../../utils/charts/utils";
const options = [
{ value: DISTRIBUTION_MODE.WEEK, label: DISTRIBUTION_MODE.WEEK },
{ value: DISTRIBUTION_MODE.MONTH, label: DISTRIBUTION_MODE.MONTH },
{ value: DISTRIBUTION_MODE.YEAR, label: DISTRIBUTION_MODE.YEAR }
];
const FeedbackDistributionCard = user => {
const [mode, setMode] = useState({
value: DISTRIBUTION_MODE.WEEK,
label: DISTRIBUTION_MODE.WEEK
});
const [{ isLoading, isError, data }] = useFeedbackDistribution(
mode.value,
[]
);
function load(data) {
switch (mode.value) {
case DISTRIBUTION_MODE.WEEK:
return dayTimeScaleProperties(data);
case DISTRIBUTION_MODE.MONTH:
return monthTimeScaleProperties(data);
default:
return yearTimeScaleProperties(data);
}
}
return (
<div className="w-full h-64 sm:w-full md:w-full lg:w-3/4 xl:w-3/4 mb-4 bg-white rounded-lg shadow">
<div className="w-full h-full ">
{isError && <ErrorSolid />}
{isLoading ? (
<Loading />
) : (
<div className="h-full w-full flex-col shadow p-6">
<div>
<p className="font-bold float-left inline-block">
Feedback by {mode.value}
</p>
<Select
className="w-40 z-50 float-right"
options={options}
onChange={e => {
setMode({ label: e.label, value: e.value });
}}
value={mode}
/>
</div>
<div className="h-full w-full">
<FeedbackDistributionLineChart
properties={load(data)}
/>
</div>
</div>
)}
</div>
</div>
);
};
export default FeedbackDistributionCard;
const FeedbackDistributionLineChart = ({ properties }) => (
<ResponsiveLine
{...properties}
enablePoints={false}
enableGridX={false}
enableGridY={false}
colors={{ scheme: "category10" }}
margin={{ top: 20, right: 30, bottom: 40, left: 40 }}
animate={true}
enableSlices={"x"}
yScale={{
type: "linear",
stacked: false
}}
axisLeft={{
legend: "total",
legendPosition: "middle",
legendOffset: -30,
tickValues: 5
}}
curve={"basis"}
enablePointLabel={true}
useMesh={true}
enableSlices={false}
/>
);
useEffect custom hook
const feedbackDistributionReducer = (state, action) => {
switch (action.type) {
case "FETCH_INIT":
return { ...state, isLoading: true, isError: false };
case "FETCH_SUCCESS":
return {
...state,
isLoading: false,
isError: false,
data: action.payload
};
case "FETCH_ERROR":
return {
...state,
isLoading: false,
isError: true,
data: action.payload
};
default:
throw new Error("Could not fetch feedback distribution");
}
};
const useFeedbackDistribution = (mode, initialData) => {
const [state, dispatch] = useReducer(feedbackDistributionReducer, {
isLoading: false,
isError: false,
data: initialData
});
useEffect(() => {
let didCancel = false;
async function load(mode) {
console.log(mode);
dispatch({ type: "FETCH_INIT", payload: null });
try {
var feedbackDistribution = firebase
.functions()
.httpsCallable("feedbackDistribution");
let result = await feedbackDistribution({ mode: mode });
var mappedResult = result.data.map(function(item) {
var info = { y: item.totalCount, x: item.time };
return info;
});
const distribution = [
{
id: "id",
data: mappedResult
}
];
if (!didCancel) {
dispatch({ type: "FETCH_SUCCESS", payload: distribution });
}
} catch (err) {
if (!didCancel) {
dispatch({ type: "FETCH_ERROR", payload: error });
}
}
}
load(mode);
return () => {
didCancel = true;
};
}, [mode]);
return [state];
};
Спасибо!