Javascript 状态更改时不渲染
我在学习如何反应。我正在尝试根据姓名对列表进行排序。Javascript 状态更改时不渲染,javascript,arrays,reactjs,sorting,Javascript,Arrays,Reactjs,Sorting,我在学习如何反应。我正在尝试根据姓名对列表进行排序。ShoppingList组件为 const ShoppingList = () => { const [items, setItems] = useState([]); const data = [ {id: 1, name: 'Soda'}, {id: 2, name: 'ice'}, ]; useEffect(() => { setItems(d
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>
console.log(sortItems)
显示已排序的数组。但不在DOM中渲染。
在返回
中,我试图以这种格式显示已排序的数据
<ul>
{items.map((item) => {
return (
<li key={item.id}>
<span>{item.name} </span>
<button onClick={() => handleRemove(item.id)}>×</button>
</li>
);
})
}
</ul>
{items.map((item)=>{
返回(
-
{item.name}
handleRemove(item.id)}>&次;
);
})
}
这里缺少什么?首先,您需要停止对初始状态使用
useffect
,
如果您想做出反应来注意您的更改,请使用对象而不是数组。(这并非总是react不会注意到您的更改,但由于您并没有更改数组,并且数组只进行了排序,所以react会忽略它)
const ShoppingList=()=>{
const[items,setItems]=useState({
数据:[
{id:1,名称:'Soda'},
{id:2,名称:'ice'},
],
});
常量handleSort=()=>{
const sortedItems=items.data.sort((a,b)=>{
const nameA=a.name.toUpperCase();
const nameB=b.name.toUpperCase();
如果(nameAnameB)返回1;
返回0;
});
设置项({
数据:分类数据表,
});
};
返回(
handleSort()}>按名称排序
{items.data.map((el,i)=>(
-
{el.name}
handleRemove(item.id)}>&次;
))}
);
}
希望这有助于我建议使用派生已排序的项目列表,因此它的“派生状态”取决于项目数组和所需的排序顺序
- 不要对初始状态使用
<代码>使用状态接受初始状态的创建者函数useffect
是一种更简洁的返回-1、0、+1进行比较的方法localeCompare
(一个[…items]
的浅拷贝)是必需的,因为items
在适当的位置对数组进行排序.sort()
const sortByName=(a,b)=>a.name.toUpperCase().localeCompare(b.name.toUpperCase());
const ShoppingList=()=>{
const[items,setItems]=useState(()=>[
{id:1,名称:“Soda”},
{id:2,名称:“ice”},
]);
const[sortOrder,setSortOrder]=使用状态(“原始”);
const sortedItems=React.useMoom(()=>{
开关(分拣机){
案例“byName”:
返回[…项]。排序(sortByName);
违约:
退货项目;
}
},[项目,分类];
返回(
setSortOrder(“byName”)}>按名称排序
setSortOrder(“原始”)}>按原始顺序排序
{sortedItems.map((el,i)=>(
-
{el.name}
&时代;
))}
);
};
如果您有兴趣更深入地了解为什么数组项会被更改(排序),但React不会呈现,有两件事需要注意:
array.sort
如何工作useState
array.sort
返回已排序的数组。请注意,数组是就地排序的,不进行复制。因此,sortItems
和items
仍然引用相同的数组
对于(2),它有点复杂,因为我们必须通读React代码库
这是签名
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
最后一部分是:
它只需使用==
操作符检查是否相等
回到您的排序函数,在我们的例子中,nextState是sortItems
,prevState是items
。记住(1),sortItems===items=>true
因此我们将跳过渲染。
这就是为什么你会看到大多数教程都说你必须做浅拷贝。
这样,您的下一个状态将与上一个状态不同
TLDR:
是上面的
,用于检查使用挂钩时的状态更改为什么要将数组包装在对象中的
项中
?这绝对不是React基于更改重新渲染所必需的。编辑并添加了说明。
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>
</>
);
}
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>
</>
);
};
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
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;
}
}
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];
}
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(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}