reactjs flushSync()在React中做什么?

xienkqul  于 2022-12-29  发布在  React
关注(0)|答案(3)|浏览(317)

我在JSConf Iceland上看过Dan Abramov的演示项目,但我不明白他为什么在下面的代码中使用flushSync

import { flushSync } from 'react-dom';

  debouncedHandleChange = _.debounce(value => {
    if (this.state.strategy === 'debounced') {
      flushSync(() => {
        this.setState({value: value});
      });
    }
  }, 1000);

flushSync在react中做什么?

wvt8vs2t

wvt8vs2t1#

flushSync会刷新整个树,并且实际上会强制对调用内部发生的更新进行完全重新呈现,因此您应该非常谨慎地使用它。这样它就不会破坏props、state和refs之间的内部一致性保证。

它还没有被正确记录。在这里阅读更多https://github.com/facebook/react/issues/11527

ocebsuys

ocebsuys2#

Chilarai有一个很好的答案!(可能是由于回答时间和现在有更多的文档)我不同意非常节省地使用它的想法。因为很多时候它将停止对额外的不必要的useEffect的需要(它也反对Dan建议如何使用useEffect https://twitter.com/dan_abramov/status/1501737272999301121)。
下面是一个基于Dan Abramov的代码的很好的例子:
这个代码的要求是一个新消息自动滚动(我目前使用这个代码,它是非常有效的)。
之前,在这个例子中,我们不能在setMessages之后滚动,因为DOM还没有呈现,需要一个钩子。

export default function App() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    let socket = connect();

    socket.onMessage((message) => {
      setMessages((m) => [
        ...m,
        message
      ])
    });
    
    return () => {
      socket.disconnect();
    };
  }, [])

  function scrollToLastMessage() {
    // ...
  }

  // NOTE: In order to scroll without using flushSync a unneeded hook
  useEffect(() => {
    scrollToLastMessage();
  }, [messages])
}

之后,在这个例子中,DOM被强制刷新同步,然后我们可以按照我们想要的顺序调用。

export default function App() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    let socket = connect();

    function scrollToLastMessage() {
      // ...
    }

    socket.onMessage((message) => {
      flushSync(() => {
        setMessages((m) => [
          ...m,
          message
        ])
      })
      scrollToLastMessage();
    });
    
    return () => {
      socket.disconnect();
    };
  }, [])
}

此代码来自丹在此视频https://www.youtube.com/watch?v=uqII0AOW1NM在35:25我建议观看!
我也强烈推荐在你的工具带上添加FlushSync!

92vpleto

92vpleto3#

经过几天的思考,我想我终于明白了。多亏了“亚历克斯·邓洛普”的回答。
如果有人想看一个API的简单使用示例,请参阅我的codesandbox example here

import { useEffect, useState } from "react";
import { flushSync } from "react-dom";

export default function App() {
  const [c, setC] = useState(0);

  const inc1 = () => {
    setC((c) => c + 1);
    console.log(document.getElementById("myId").innerText); // old value
  };

  const inc2 = () => {
    flushSync(() => {
      setC((c) => c + 1);
    });
    console.log(document.getElementById("myId").innerText); // new value
    // However below log will still point to old value
    // console.log(c); // old value
  };

  return (
    <div className="App">
      Count: <div id="myId">{c}</div>
      <button onClick={inc1}>without flushSync</button>
      <button onClick={inc2}>with flushSync</button>
    </div>
  );

此外,flushSync的另一个方面是,必须***不要***在useEffect钩子中使用它,因为react会抛出关于它的警告,即Warning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.

const BadPattern = () => {
  const [c, setC] = useState(0);

  // YOU CAN NOT USE `flushSync` in useEffect hook
  // FROM REACT: Warning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.
  useEffect(() => {
    flushSync(() => {
      setC((c) => c + 1);
    });
    console.log(c);
  }, []);

  return (
    <div className="App">
      <h2>BAD PATTERN</h2>
      Count: <div>{c}</div>
    </div>
  );
};

相关问题