【问题标题】:How to synchronously change state within useState如何在 useState 中同步更改状态
【发布时间】:2020-07-24 20:16:03
【问题描述】:

大家好,我是使用 React Hooks 的新手,但无法直接在 google 上找到它。我正在尝试嵌套 setState 回调,以便状态可以同步更新,但由于我得到未定义的值而没有成功。状态中的值依赖于我状态中的其他值,所以我希望它同步运行。我尝试将它嵌套在下面,当它是一个类组件并使用 this.setState 并使用回调时,它可以工作,但是当我尝试在功能类中使用反应挂钩时它不起作用。

这是我的代码:

const [state, setState] = useState({numCols: 0, numRows: 0, cardWidth: 0, cardHeight: 0, containerWidth: 0});
  
  const {
    sheetList,
    sheetsTotalCount,
    sheetsMetadata,
    id,
    projectId,
    onEditButtonClick,
    handleInfiniteLoad,
    sheetsAreLoading,
    classes,
    permissions,
    onItemClick,
  } = props;

  const setCardSize = ({ width }) {
    setState({
      containerWidth: width > 0 ? width : defaultWidth
    },() => { 
      setState({numCols: Math.max(Math.floor(state.containerWidth / baseThumbnailWidth), 1) }, () => {
        setState({numRows: Math.ceil(sheetList.size / state.numCols)}, () => {
          setState({cardWidth: Math.floor(state.containerWidth / state.numCols - 2 * marginBetweenCards)}, () => {
            setState({cardHeight: Math.round(state.cardWidth * thumbnailProportion)});
          });
        });
      });
    });
  }

理想情况下,我希望首先更新 containerWidth 变量,然后是 numCols 变量,然后是 cardWidth,然后是 cardHeight。有什么方法可以同步执行此操作,这样我就不会得到未定义的值?

【问题讨论】:

    标签: reactjs react-hooks use-state


    【解决方案1】:

    我对您想要实现的目标感到有些困惑。但不要忘记,与类不同,您每次都必须将所有属性设置为状态。

    setState({numCols: Math.max(Math.floor(state.containerWidth / baseThumbnailWidth), 1) }
    

    此代码将仅用 numCols 替换您的所有状态。您希望那里的其余状态像这样,现在只有 numCols 会改变,其他一切都将相同。

    setState({...state, numCols: Math.max(Math.floor(state.containerWidth / baseThumbnailWidth), 1) }
    

    接下来要记住的是,如果您想在一个渲染中多次更改状态,请使用此表单:

    setState(oldState => {...oldState, newValue: 'newValue'});
    

    这将允许在一个渲染中使用最后一个值设置而不是在最后一个渲染上对状态进行多次更新。例如:

    const [state, setState] = useState(0); // -> closed on this state's value!
    
    setState(state + 1);
    setState(state + 1);
    setState(state + 1); //State is still 1 on next render 
    // because it is using the state which happened on the last render.
    // When this function was made it "closed" around the value 0
    // (or the last render's number) hence the term closure.
    

    对比这个:

    const [state, setState] = useState(0);
    
    setState(state => state + 1);
    setState(state => state + 1);
    setState(state => state + 1); //State is 3 on next render.
    

    但是为什么不同步计算这些值呢?

      const setCardSize = (width) => {
        const containerWidth = width > 0 ? width : defaultWidth;
        const numCols = Math.max(Math.floor(containerWidth / baseThumbnailWidth), 1);
        const numRows = Math.ceil(sheetList.size / numCols);
        const cardWidth = Math.floor(containerWidth / numCols - 2 * marginBetweenCards);
        const cardHeight = Math.round(cardWidth * thumbnailProportion);
        setState({containerWidth, numCols, numRows, cardWidth, cardHeight});
      }
    

    查看它讨论的docs

    与类组件中的 setState 方法不同,useState 可以 不会自动合并更新对象。

    如果新状态是使用前一个状态计算的,你可以传递一个 函数设置状态。该函数将接收先前的值, 并返回一个更新的值。这是一个计数器组件的示例 使用两种形式的 setState:

    【讨论】:

      【解决方案2】:

      鉴于您正在计算大量依赖于另一个变量的变量,并且您希望状态全部同时更新,为什么不将计算分开并在最后设置一次状态呢?更具可读性,并且只需要一次 setState 调用。

       const setCardSize = ({ width }) => {
          const containerWidth = width > 0 ? width : defaultWidth;
          const numCols = Math.max(Math.floor(containerWidth / baseThumbnailWidth), 1,);
          const numRows = Math.ceil(sheetList.size / numCols);
          const cardWidth = Math.floor(containerWidth / numCols - 2 * marginBetweenCards);
          const cardHeight = Math.round(cardWidth * thumbnailProportion);
          setState({ containerWidth, numCols, numRows, cardWidth, cardHeight });
        };
      

      但要回答实际问题,如果您想让一个变量的状态更新立即(或您所说的“同步”)更新另一个状态变量,请使用useEffect

      您只需给useEffect 两个参数:一个在每次因变量发生变化时运行的函数,然后是一个需要关注的变量数组。

      每个状态变量都有自己的useState,而不是我在这里也做过的一个大对象,这样更简洁(更快,更不容易出错,并且通常建议用于功能组件)。

        const [containerWidth, setContainerWidth] = useState(0);
        const [numCols, setNumCols] = useState(0);
        const [numRows, setNumRows] = useState(0);
        const [cardWidth, setCardWidth] = useState(0);
        const [cardHeight, setCardHeight] = useState(0);
      
        const setCardSize = ({ width }) => setContainerWidth(width > 0 ? width : defaultWidth)
        useEffect(() => setNumCols(Math.max(Math.floor(containerWidth / baseThumbnailWidth), 1)) , [containerWidth])
        useEffect(() => setNumRows(Math.ceil(sheetList.size / numCols)), [numCols])
        useEffect(() => setCardWidth(Math.floor(containerWidth / numCols - 2 * marginBetweenCards)), [containerWidth])
        useEffect(() => setCardHeight(Math.round(cardWidth * thumbnailProportion)), [cardWidth])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-10
        • 1970-01-01
        • 2021-07-29
        • 2020-07-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多