【发布时间】:2020-05-25 01:14:39
【问题描述】:
我有一个 React 功能组件,它呈现一个 JSX,其中包含一个段落、一个来自 Select React JS 库的下拉列表和一个来自 Nivo 库的折线图。
当我第一次渲染组件时,我有一个 useEffect 钩子,它可以帮助我从后端检索数据,一个列表。数据基于 mode 值从后端返回。
这个模式可以是:
- 周
- 月
- 年
我有一个 useState const [mode, setMode] = useState("week")。
“选择”下拉菜单有 onChange 事件侦听器,可帮助我设置新模式
setMode(e.value) - 可以是周、月或年
当我设置新模式时,它会返回后端并检索包含数据的新列表。
我的问题如下:
- 我从下拉列表中选择一个新值
- 我的组件重新渲染并获取我的数组的当前值
- 然后它将 mode 更改为新值
- 然后
useEffect根据新模式获取新数据,并用新列表重新渲染。
例如:
我当前的模式值是week。数据来自后端并在图表中正确呈现。我选择了一个新模式,月并致电setMode("month")。
首先发生的事情:
- 组件重新渲染
- 获取周图表的当前数据
-
useEffect对新模式月份数据发出 GET 请求,并在图表上设置新数据 - 如果当前模式是 month 并且我想显示一个带有 year 的图表。再次在下拉
onChange上,我将模式设置为 year,但第一个组件重新渲染获取当前数组值,即 month,然后返回 year。
我不想每次从下拉列表中选择一个新值时都先获取数组的当前值。我只想使用新值设置模式,并使用新列表从服务器检索数据。
这是我的代码:
反应组件:
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 自定义钩子
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];
};
谢谢!
【问题讨论】:
标签: reactjs react-hooks jsx