reactjs - 我应该把一些操作放在功能组件的哪里
问题描述
我从 grpahql 端点获取了一些数据。在渲染之前我必须做一些计算。但是这段代码在多次渲染这个组件后会导致浏览器冻结。我想我可以把这段代码变成useEffect。但我不确定为什么浏览器会冻结以及如何修改代码以清洁和运行良好。如您所见,这段代码有些错误。你能解释一下怎么做吗?
const StatusBar = () => {
const { data: curProgram_data } = useQuery(GET_CUR_PROGRAM);
const { data: programs_data, loading: programs_loading } = useQuery(GET_PROGRAMS);
const { data: tests_data, loading: tests_loading } = useQuery(GET_TESTS);
const { data: articles_data, loading: articles_loading } = useQuery(GET_ARTICLES);
if (programs_loading || tests_loading || articles_loading)
return <p>Loading ... </p>;
const { curProgram } = curProgram_data;
const { programs } = programs_data;
const totalTests = tests_data.tests;
const totalArticles = articles_data.articles;
var startYear = moment(totalTests[0].date, 'YYYY/MM/DD').format('YYYY');
var endYear = moment(totalTests[totalTests.length - 1].date, 'YYYY/MM/DD').format('YYYY');
var years = [];
for(var i = startYear; i <= endYear; i ++)
years.push(i);
for(var i = 0; i < programs.length; i ++) // Sort Programs by Date
for(var j = i + 1; j < programs.length; j ++) {
if(programs[i].tests[0].date > programs[j].tests[0].date) {
var temp;
temp = programs[i];
programs[i] = programs[j];
programs[j] = temp;
}
}
console.log("I think we have to turn all variables to state with useEffect");
var programsGroupedByYear = [];
var eachProgramLength = [];
var startYear = moment(programs[0].tests[0].date, 'YYYY/MM/DD').format('YYYY');
var endYear = moment(programs[programs.length - 1].tests[0].date, 'YYYY/MM/DD').format('YYYY');
for(i = 0; i < endYear - startYear + 1; i ++) {
programsGroupedByYear[i] = [];
eachProgramLength[i] = [];
}
var isNext = startYear;
var yearIt = 0;
for(i = 0; i < programs.length; i ++) { // Group Programs by Year
var year = moment(programs[i].tests[0].date, 'YYYY/MM/DD').format('YYYY');
if(year == isNext)
programsGroupedByYear[yearIt].push(programs[i]);
else {
isNext = year;
yearIt ++;
programsGroupedByYear[yearIt].push(programs[i]);
}
}
... ....
return (
<div>
<YearBar
groupLengths = {groupLengths}
curProgramPos = {curProgramPos}
years = {years}
totalArticles = {totalArticles}
/>
....
</div>
)
}
解决方案
React 强调所有动态数据都必须处于其状态这一事实。这甚至包括诸如表单输入值之类的 DOM 数据。你在这里做的是完全错误的。
你在这里犯的错误太多了
错误一:
不使用反应状态。这一点怎么强调都不过分。你所有的动态数据都必须进入 React 状态 - year
,totalTests
- 一切都必须声明为 React 状态。这样 React 可以跟踪数据变化并优化性能。
错误2:
不useEffect()
用于执行副作用。不能在函数的根部执行副作用。你不应该。React 永远不会知道你的函数内部到底发生了什么。使用正确useEffect()
的依赖数组
错误3:
这是 JavaScript 错误。不要var
用于声明变量。有范围问题。用于let
变量和const
常量。
错误4:
不使用高效的内置 JavaScript 方法,而是编写自己的排序和其他算法。
此组件中的控制台日志调用不定式。为什么?
因为 console.log 是函数的根目录。每次有更新并且 React 需要重新渲染组件时,都会使用新的状态值再次调用该函数。由于您的代码使函数陷入无限循环,因此控制台记录了无限次。useState
这就是为什么使用and更重要的原因useEffects
,以便 React 知道要执行什么以及要更新什么,并防止自己陷入无限循环。
希望它能回答你的问题,下一步转向解决方案
解决方案
我必须明确表示我不能也不会重写你的整个代码并确保一切正常,这是你的工作,但我非常乐意帮助你并解释它们。
在转向 React 之前,让我先清理一下 JavaScript 空间
避免使用var
var
在 JavaScript 中没有正确限定范围,这会在调试时导致很多困惑和头痛。所以使用新的关键字来声明变量
let
对于变量const
为常数
它们的范围和行为与您期望它们的行为方式相同。
如果存在某些内容,请不要重写它 - 使用内置方法
不要编写自己的排序算法,而是使用 JavaScript 内置sort
方法。拥抱有趣的函数式编程
您的排序算法可以替换为
const sortedPrograms = [ ...programs ].sort((a, b) =>
a.tests[0].date > b].tests[0].date ? -1 : 1
)
我使用[ ...programs ].sort
而不是的原因programs.sort
是因为sort
方法会改变原始数组,这在 React 中是不可取的,这就是我浅克隆数组的原因。
对于接下来的两个 for 循环,我相信您正在尝试根据其年份对程序进行分组。(如我错了请纠正我)。
那几十行代码可以很容易地用我最喜欢的 JavaScriptreduce
方法代替。
您的分组算法可以替换为
const programsGroupedByYear = programs.reduce(
// acc - Accumulated, cur - current item
(acc, cur) => {
const year = moment(cur.tests[0].date, 'YYYY/MM/DD').format('YYYY');
return {
...acc,
// Check if the year index exist in the accumulated object
// if yes, append it to the array
// if no, create a new array
// and store in the year index
[year]: acc[year] ? [ ...acc[year], cur ] : [cur]
}
}
, {})
就是这样,你的 20+ 行两个 for 循环可以替换为 6+ 行Array.reduce
方法。
将所有数据存储在 React State 中
ApollouseQuery
是一个自定义钩子,这意味着输出data
是一个 React 状态。所以需要在这里声明任何新的状态,除了变量来存储你的排序数据和分组数据。
将所有副作用移到里面useEffects
函数的根本不应该有任何副作用。他们都必须进去useEffects
useEffect(() => {
// All your side effects goes in here
}, [deps])
// React will track the items in dep array,
// if there's any change in them, React will call this useEffect
如果函数的根不允许有副作用,那么根应该允许什么?这让我们
始终在函数的根部使用钩子
不要在循环、条件或嵌套函数中调用 Hook。
最终建议
使用 JavaScript 作为函数式编程方式,特别是如果您使用 React 而不是考虑 for 循环和数据变异
我强烈建议您使用Rule of Hooks linter 插件。
所以总结,
您的代码的 GIST
const StatusBar = () => {
const { data: curProgram_data } = useQuery(GET_CUR_PROGRAM);
const { data: programs_data, loading: programs_loading } =
useQuery(GET_PROGRAMS);
const { data: tests_data, loading: tests_loading } = useQuery(GET_TESTS);
const { data: articles_data, loading: articles_loading } =
useQuery(GET_ARTICLES);
const [sortedProgram, setSortedProgram] = useState([]);
const [programsGroupedByYear, setProgramsGroupByYear] =
useState([]);
useEffect(() => {
if (!programs_loading) {
const _sortedPrograms = [ ...programs_data.programs ].sort((a, b) =>
a.tests[0].date > b].tests[0].date ? -1 : 1
)
setSortedProgram(_sortedPrograms)
const _programsGroupedByYear = programs.reduce(
(acc, cur) => {
const year = moment(cur.tests[0].date, 'YYYY/MM/DD').format('YYYY');
return {
...acc,
[year]: acc[year] ? [ ...acc[year], cur ] : [cur]
}
}
, {})
}
setProgramsGroupedByYear(__programsGroupedByYear)
}, [programs_data, programs_loading])
// Then place your if condition
if (programs_loading || tests_loading || articles_loading)
return <p>Loading ... </p>;
return (
<YourJSXCode />
)
}
推荐阅读
- spring-boot - 在 GCP 上使用 NGINX 入口控制器缺少自定义标头
- autohotkey - AUTOHOTKEY - 如何使用操纵杆发送控制 K
- java - 我的 expandableListview 没有扩展
- c# - 如何使用 cron 表达式中的 asp.net mvc Web 应用程序创建 Windows 调度程序作业?
- php - Android表情转HTML格式
- php - session 在codeigniter中从HTTP切换到HTTPS时不存储
- ios - 从 Android 调用 Sinch Callkit 项目?
- android - 如何将 firebase 测试实验室与 circleci 集成
- c# - 无法在 while 循环 C# 中激活布尔值或停用它们
- c# - Url.Action 未命中控制器方法