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}&nbsp;</span>
              <button onClick={() => handleRemove(item.id)}>&times;</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
    <代码>使用状态接受初始状态的创建者函数
  • 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,名称:“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
  • 对于(1),很容易,
    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 use function
    是上面的
    ,用于检查使用挂钩时的状态更改
  • 使用数组、对象(如果使用钩子)时,始终进行浅复制

  • 为什么要将数组包装在对象中的
    项中
    ?这绝对不是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}&nbsp;</span>
                            <button onClick={() => handleRemove(item.id)}>&times;</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}&nbsp;</span>
                <button>&times;</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
      );
    }