我有一个对象数组,这些对象显示在网格中。目标是对数据使用多个过滤器-一个过滤器在特定列中按文本,一个在不同日期范围内,然后按升/降序按各列排序,并且还具有分页功能(我已经为此提供了辅助功能到位)。我的第一种方法是具有多个useEffects,在其中对数据进行过滤/排序并更新状态。问题在于,设置状态的函数显然不会立即更新状态,因此在下一个useEffect中,我不使用由前一个数据过滤的结果,而是对原始数据的过滤。基本上,我需要在原始数组上链接这3种过滤方法,然后进行排序,然后按此顺序分页,但是除非用户更改了设置,否则我不想进行某些过滤/排序。最好的方法来解决此问题的想法。这是一个快速的无效原型。 https://codesandbox.io/s/peaceful-nigh-jysdy用户可以随时更改过滤器,并且所有3个都应生效,我还需要设置一个初始配置来保存初始过滤器值
您的代码有2个主要问题:
让我解释两个问题。
您在化简器的初始状态下有n个项目。
[
{ id: "124", name: "Michael", dateCreated: "2019-07-07T00:00:00.000Z" },
{ id: "12", name: "Jessica", dateCreated: "2019-08-07T00:00:00.000Z" },
{ id: "53", name: "Olivia", dateCreated: "2019-01-07T00:00:00.000Z" }
]
[当您dispatch
一个动作(例如FILTER_BY_TEXT
)时,您正在生成一个新的过滤数组:
dispatch({ type: "FILTER_BY_TEXT", payload: { text: 'iv' } });
给予:
[ { id: "53", name: "Olivia", dateCreated: "2019-01-07T00:00:00.000Z" } ]
但是如果您随后分派新值(在这种情况下为ae
,因此它应该与Michael匹配:]
dispatch({ type: "FILTER_BY_TEXT", payload: { text: 'ae' } });
您得到一个空数组!
[]
这是因为您要在[[已过滤]]列表上应用ae
过滤器!卷积数据流
function reducer(state, action) {
switch (action.type) {
case 'FILTER_BY_XXX': return filterByXXX(state, action);
default: return state;
}
}
function filterByXXX(state, action) { … }
function App() {
const [state, dispatch] React.useReducer(reducer, []);
// (1) Double state “slots”
const [xxx, setXXX] = React.useState(INITIAL_XXX);
const [inputXXX, setInputXXX] = React.useState(INITIAL_INPUT_XXX);
// (2) Synchronization effects (to apply filters)
React.useEffect(() => {
dispatch({ type: 'FILTER_BY_XXX', payload: xxx });
}, [xxx]);
return (
<input
value={inputXXX}
onChange={event => {
// (4) Store the raw input value
setInputXXX(event.currentTarget.value);
// (5) Store a computed “parsed” input value
setXXX((new Date(event.currentTarget.value));
}}
/>
);
}
让我展示一下谬论:
对于字符串值,例如filterByText
和inputVal
,这是很明显的,但是对于“已解析”的值,您需要一点解释。
确实
确实
可以将UI驱动状态与“已解析”状态分开,但是您无需将其存储在状态中!实际上,您始终可以根据实际输入状态重新计算它们。请看一下React文档的这一部分:Main Concepts › Thinking in React › Identify The Minimal (but complete) Representation Of UI State“正确的方法是在(1)处移除双槽,并在(5)处移除二传手。然后,您可以
在渲染时重新计算值
:const [inputXXX, setInputXXX] = React.useState(INITIAL_INPUT_XXX);
// Here we just recompute a new Date value from the inputXXX state
const xxx = new Date(inputXXX);
return (
<input
value={inputXXX}
onChange={event => {
setInputXXX(event.currentTarget.value);
}}
/>
);
。每次调度都指示应用程序在先前应用的过滤器之上添加
- 您的reducer,如前所述,从完整的数据集开始,并且在每次调度时都进行了[[强制过滤]
another过滤器。之所以会这样,是因为在这种状态下,您只有先前操作的结果。这类似于将长算术运算视为一连串简单运算的方式:
11 + 23 + 560 + 999 = 1593
可以改写为
的信息,但是仍然看到结果了!( ( ( 11 + 23 ) + 560 ) + 999 ) = 1593 ( ( ( 34 ) + 560 ) + 999 ) = 1593 ( ( 594 ) + 999 ) = 1593
在每一步上,您失去的都是关于[[how
[当您的应用程序要“按文本过滤”时,它不想“添加”过滤器,而是将以前的文本过滤器替换为新的文本过滤器。都在这种情况下位于相同的位置(或非常靠近),则可能是没有明显的原因使代码过于复杂。您通过使“从状态到减速器的状态”与效果同步来对状态变化做出反应。
这的确是useEffect的非常正确的用法,但是如果要同步的值[[from和
to
return <input onChange={event => { dispatch( … ); } />;
但是真正的问题是下一个。您将计算结果存储为“状态”,但实际上是“计算值”。您的[[real
您需要使自己摆脱对渲染时计算值的恐惧:
const [inputVal, setInputVal] = React.useState("");
const [selectTimeVal, setSelectTimeVal] = React.useState("");
const [selectSortVal, setSelectSortVal] = React.useState("");
const data = [ {…}, {…}, {…} ];
let displayData = data;
displayData = filterByText(displayData, inputVal);
displayData = filterByTimeFrame(displayData, selectTimeVal);
displayData = sortByField(displayData, selectSortVal);
return (
<>
<input value={inputVal} onChange={………} />
<select value={selectTimeVal} onChange={………} />
<select value={selectSortVal} onChange={………} />
{displayData.map(data => <p key={data.id}>{data.name}</p>)}
</>
);
一旦您理解了上面列出的主题,您可能希望避免无用的重新计算,但是只有在您实际遇到一些性能问题时才可以! (请参见the very detailed instructions about performance optimizations in the React docs)如果您到了这一点,您会发现以下React API和Hooks非常有用:
React.PureComponent
React.PureComponent
React.Component
’s shouldComponentUpdate
method
React.Component
shouldComponentUpdate