我正在学习反应。我正在尝试根据名称对列表进行排序。 ShoppingList
组件是
const ShoppingList = () => {
const [items, setItems] = useState([]);
const data = [
{id: 1, name: 'Soda'},
{id: 2, name: 'ice'},
];
useEffect(() => {
setItems(data);
}, []);
const handleSort = () => {}
return ();
}
单击按钮,我正在尝试对数据进行排序并显示它。
<button onClick={() => handleSort()}>Sort by name</button>
在 handleSort()
函数内部
const sortItems = items.sort((a, b) => {
const nameA = a.name.toUpperCase();
const nameB = b.name.toUpperCase();
if(nameA < nameB)
return -1;
if(nameA > nameB)
return 1;
return 0;
});
console.log(sortItems);
setItems(sortItems);
console.log(sortItems)
显示已排序的数组。但不在 DOM 中渲染。
在 return
中,我试图以这种格式显示排序后的数据
<ul>
{items.map((item) => {
return (
<li key={item.id}>
<span>{item.name} </span>
<button onClick={() => handleRemove(item.id)}>×</button>
</li>
);
})
}
</ul>
我在这里缺少什么?
答案 0 :(得分:2)
我建议使用 useMemo
派生项目的排序列表,因此它的“派生状态”取决于项目数组和所需的排序顺序。
useEffect
作为初始状态。 useState
接受初始状态的创建者函数。localeCompare
是返回 -1、0、+1 进行比较的更简洁的方法。[...items]
(items
的浅拷贝)是必需的,因为 .sort()
对数组进行就地排序。const sortByName = (a, b) => a.name.toUpperCase().localeCompare(b.name.toUpperCase());
const ShoppingList = () => {
const [items, setItems] = useState(() => [
{ id: 1, name: "Soda" },
{ id: 2, name: "ice" },
]);
const [sortOrder, setSortOrder] = useState("original");
const sortedItems = React.useMemo(() => {
switch (sortOrder) {
case "byName":
return [...items].sort(sortByName);
default:
return items;
}
}, [items, sortOrder]);
return (
<>
<button onClick={() => setSortOrder("byName")}>Sort by name</button>
<button onClick={() => setSortOrder("original")}>Sort in original order</button>
<ul>
{sortedItems.map((el, i) => (
<li key={el.id}>
<span>{el.name} </span>
<button>×</button>
</li>
))}
</ul>
</>
);
};
答案 1 :(得分:1)
首先你需要停止使用 useEffect
作为初始状态,
如果您想做出反应以注意到您的更改,请使用对象而不是数组。 (这并不总是反应不会注意到您的更改,但由于您没有更改数组并且它只是排序,因此反应会忽略它)。
const ShoppingList = () => {
const [items, setItems] = useState({
data: [
{ id: 1, name: 'Soda' },
{ id: 2, name: 'ice' },
],
});
const handleSort = () => {
const sortedItems = items.data.sort((a, b) => {
const nameA = a.name.toUpperCase();
const nameB = b.name.toUpperCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
setItems({
data: sortedItems,
});
};
return (
<>
<button onClick={() => handleSort()}>Sort by name</button>
<ul>
{items.data.map((el, i) => (
<li key={el.id}>
<span>{el.name} </span>
<button onClick={() => handleRemove(item.id)}>×</button>
</li>
))}
</ul>
</>
);
}
希望对你有帮助?
答案 2 :(得分:1)
如果您有兴趣更深入地了解为什么数组项已更改(已排序)但 React 不呈现,请注意以下两点:
array.sort
的工作原理useState
重新渲染对于 (1),很简单,array.sort
返回排序后的数组。请注意,数组是就地排序的,并且没有复制。因此 sortItems
和 items
仍然指向同一个数组
对于 (2),它有点复杂,因为我们必须通读 React 代码库。
这是React.useState签名
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
使用 Github 导航工具扫描我们最终会到达 this code:
useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
currentHookNameInDev = 'useState';
mountHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountState(initialState);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
}
接下来我们必须找到mounState:的定义
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
...
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}
注意,mountState 的返回类型是一个数组,其中第二个参数是一个函数,就像 const [items, setItems] = useState([])
这意味着我们快到了。
dispatch 是来自 dispatchAction.bind
扫描我们将在这一行结束的代码:
if (is(eagerState, currentState)) {
// Fast path. We can bail out without scheduling React to re-render.
// It's still possible that we'll need to rebase this update later,
// if the component re-renders for a different reason and by that
// time the reducer has changed.
return;
}
最后一部分是 function is 的作用:
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}
它只是使用 ===
运算符检查相等性。
回到排序函数,在我们的例子中 nextState 是 sortItems
,prevState 是 items
。考虑到 (1),sortItems === items => true
所以 React 将跳过渲染。
这就是为什么你看到大多数教程都说你必须做浅拷贝。
通过这样做,您的 nextState 将与您的 prevState 不同。
TLDR:
is
来检查使用钩子时的状态变化