【问题标题】:React: If a useState is updated, how can it be also non-updated at the same time?React:如果一个 useState 更新了,怎么可能同时不更新呢?
【发布时间】:2020-02-10 15:59:01
【问题描述】:

我有一个反应代码

  • 设置empty state
  • fills国家
  • 一旦这个状态被填充,它renders一些图像
  • 然后此图像会触发 onLoad 事件
  • 这个 onLoad 事件然后调用 function 那个 reads 初始 state
  • 但是这个状态是empty

这怎么可能?如果函数被调用,则表示状态不再为空

https://codesandbox.io/s/usestate-strange-things-9rydu

代码

import React, { useRef, useState, useEffect } from "react";
import styled from "@emotion/styled";

const useMyHook = (virtual_structure, setVirtual_structure) => {
  useEffect(() => {
    console.log("virtual_structure is updated!");
    console.log(virtual_structure);
    console.log("____virtual_structure is updated!");
  }, [virtual_structure]);

  const refs = useRef([]);

  const createStructure = () => {
    console.log("virtual_structure, is it empty?");
    console.log(virtual_structure);
  };

  useEffect(() => {
    createStructure();
  }, []);

  const assignRef = r =>
    r && (refs.current.includes(r) || refs.current.push(r));

  return [assignRef, createStructure];
};

export default function App() {
  const [virtual_structure, setVirtual_structure] = useState([]);

  const [assignRef, updateGrid] = useMyHook(
    virtual_structure,
    setVirtual_structure
  );

  useEffect(() => {
    const temp_structure = Array.from({ length: 4 }, () => ({
      height: 0,
      cells: []
    }));
    temp_structure[0].cells = Array.from({ length: 10 }, () => {
      const rand = Math.random();
      const r = rand > 0.1 ? parseInt(500 * rand) : parseInt(500 * 0.1);
      return {
        height: "",
        el: (
          <div ref={assignRef}>
            <Image
              alt=""
              onload={updateGrid}
              num=""
              src={`https://picsum.photos/200/${r}`}
            />
          </div>
        )
      };
    });

    setVirtual_structure(temp_structure);
  }, []);

  return (
    <Container>
      {virtual_structure.map((col, i) => (
        <div key={`col${i}`}>
          {col.cells && col.cells.map((cell, j) => <>{cell.el}</>)}
        </div>
      ))}
    </Container>
  );
}

const Image = ({ alt, onload, num, src }) => (
  <>
    <Label>{num}</Label>
    <Img src={src} alt={alt} onLoad={onload} />
  </>
);

const Img = styled.img`
  border: 1px solid #000;
  height: min-content;
  margin: 0;
  padding: 0;
`;
const Label = styled.div`
  position: absolute;
`;

const Container = styled.div`
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  background: #ccc;
  align-content: center;

  div {
    flex: 1;

    div {
      color: #fff;
      font-weight: 700;
      font-size: 32px;
      margin: 4px;
    }
  }
`;

还有console.log

virtual_structure is updated!
index.js:27 Array(0)length: 0__proto__: Array(0)
index.js:27 ____virtual_structure is updated!
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure is updated!
index.js:27 Array(4)0: {height: 0, cells: Array(10)}1: {height: 0, cells: Array(0)}2: {height: 0, cells: Array(0)}3: {height: 0, cells: Array(0)}length: 4__proto__: Array(0)
index.js:27 ____virtual_structure is updated!
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)length: 0__proto__: Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 []length: 0__proto__: Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 []

【问题讨论】:

  • 您需要将您在挂钩中使用的所有变量传递到挂钩的第二个参数的数组中。例如。 useHook(()=&gt;{...},[assignRef, updateGrid, setVirtual_structure]。如果不这样做,将使您的变量过时并产生许多其他难以调试的错误
  • 您能否提供一个示例或指向一些文档?这种语法我认为它是 useEffect 等人独有的
  • 确实是关于useEffect。我说 useHooks 是为了让你也注意你自己创建的钩子!

标签: javascript reactjs react-hooks react-state


【解决方案1】:

这是由于closures而发生的。

您将updateGrid 函数传递给每个Image 组件安装一次

// useEffect closes its lexixal scope upon "updateGrid" 
const [assignRef, updateGrid] = useMyHook(
  virtual_structure,
  setVirtual_structure
);

useEffect(() => {
  ...
  temp_structure[0].cells = Array.from({ length: 10 }, () => {
    return {
      el: (
        <div ref={assignRef}>
//                          v Used inside the callback scope
          <Image onload={updateGrid} />
        </div>
      )
    };
  });

  setVirtual_structure(temp_structure);
}, []);

但是,updateGrid(即您重命名的createStructure)中virtual_structure 的值实际上总是等于[]useEffect 回调的词法范围内。尽管createStructure 确实在渲染时进行了更新,但它从未传递Image 组件具有预期值

const createStructure = () => {
  console.log('virtual_structure, is it empty?');
  console.log(virtual_structure); // always virtual_structure=[]
};

附注:永远不要忽略 lint 警告,虽然您可能知道自己在做什么,但它可能会导致意外错误。

【讨论】:

  • 是的,现在我明白了(非常感谢!),但是我该怎么做呢?尝试应用@japrescott 的建议但没有成功(到目前为止)
  • 你需要修复逻辑,我会重写所有内容
  • (已提出替代方案)
【解决方案2】:

正如@Dennis-vash 回答中所说,“闭包”将useState 变量冻结在作用域函数中,因此该函数永远不会看到该变量的当前(更新)值

一种解决方法是,我总是可以调用一个更新状态的函数,而不是调用一个执行逻辑的函数,然后使用这个状态来触发该函数(现在是一个 useEffect 而不是一个函数)


如果有人想提出更好的替代方案来解决这个问题,我会将这个问题留几天(?)


代码

https://codesandbox.io/s/usestate-strange-things-tneue

import React, { useRef, useState, useEffect } from "react";
import styled from "@emotion/styled";

const useMyHook = (virtual_structure, setVirtual_structure, updateGrid) => {
  const refs = useRef([]);

  useEffect(() => {
    console.log("virtual_structure, is it empty?");
    console.log(virtual_structure);
  }, [updateGrid, virtual_structure]);

  const assignRef = r =>
    r && (refs.current.includes(r) || refs.current.push(r));

  return [assignRef];
};

export default function App() {
  const [virtual_structure, setVirtual_structure] = useState([]);
  const [updateGrid, setUpdateGrid] = useState();

  const [assignRef] = useMyHook(
    virtual_structure,
    setVirtual_structure,
    updateGrid
  );

  const update = async () => setUpdateGrid(updateGrid + 1);

  useEffect(() => {
    const temp_structure = Array.from({ length: 4 }, () => ({
      height: 0,
      cells: []
    }));
    temp_structure[0].cells = Array.from({ length: 10 }, () => {
      const rand = Math.random();
      const r = rand > 0.1 ? parseInt(500 * rand) : parseInt(500 * 0.1);
      return {
        height: "",
        el: (
          <div ref={assignRef}>
            <Image
              alt=""
              onload={update}
              num=""
              src={`https://picsum.photos/200/${r}`}
            />
          </div>
        )
      };
    });

    setVirtual_structure(temp_structure);
  }, []);

  return (
    <Container>
      {virtual_structure.map((col, i) => (
        <div key={`col${i}`}>
          {col.cells &&
            col.cells.map((cell, j) => (
              <React.Fragment key={`cell${j}`}>{cell.el}</React.Fragment>
            ))}
        </div>
      ))}
    </Container>
  );
}

const Image = ({ alt, onload, num, src }) => (
  <>
    <Label>{num}</Label>
    <Img src={src} alt={alt} onLoad={onload} />
  </>
);

const Img = styled.img`
  border: 1px solid #000;
  height: min-content;
  margin: 0;
  padding: 0;
`;
const Label = styled.div`
  position: absolute;
`;

const Container = styled.div`
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  background: #ccc;
  align-content: center;

  div {
    flex: 1;

    div {
      color: #fff;
      font-weight: 700;
      font-size: 32px;
      margin: 4px;
    }
  }
`;

【讨论】:

    猜你喜欢
    • 2023-03-03
    • 2019-10-19
    • 2020-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-01-07
    • 1970-01-01
    • 2021-03-03
    • 2021-11-15
    相关资源
    最近更新 更多