reactjs 在useEffect内部初始化useState有什么区别?

camsedfj  于 2023-01-17  发布在  React
关注(0)|答案(1)|浏览(150)
const [state, set_state] = useState(1);

对比

const [state, set_state] = useState();
useEffect(() => {
  set_state(1);
}, [])

就更新实际发生的周期而言,两者之间是否有真实的的区别?使用useEffect是否会导致额外的周期等?

deyfvvtc

deyfvvtc1#

您可能正在寻找惰性初始状态函数参数:

惰性初始状态

initialState参数是在初始呈现期间使用的状态。在后续呈现中,将忽略该参数。如果初始状态是高开销计算的结果,则可以提供一个函数来代替,该函数将仅在初始呈现时执行:

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

但是,不只是提供一个链接,让我们检查您所展示的代码的不同行为(除了lazy initialstate函数参数之外)。
下面是有关代码示例的说明:
它们使用一个自定义helper钩子来记录组件呈现时间和状态值更新时间的信息,输出被序列化为JSON,因此当状态值为undefined时,它在JSON中表示为null(因为undefined不是有效的JSON类型)由于Stack Overflow的代码片段机制的限制,有很多重复的样板文件-焦点代码在App函数的主体中。

正在创建用值初始化的状态:

const [state, setState] = useState(1);

在这种情况下,状态初始化一次,并且从不更新。组件呈现一次:

<style>.json { background-color: hsla(0, 0%, 50%, 0.15); font-family: monospace; font-size: 1rem; padding: 0.5rem; }</style>
<div id="root"></div><script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.development.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.development.js"></script><script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.20.12/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

const { useCallback, useRef, useState } = React;

function VisualJson ({ data }) {
  const json = JSON.stringify(
    data,
    // undefined doesn't serialize, so null is used as a substitute:
    (_key, value) => typeof value === "undefined" ? null : value,
    2,
  );
  return (<pre className="json"><code>{ json }</code></pre>);
}

function useComponentUpdateMeta (state) {
  const ref = useRef({
    updates: [],
    get totalRenders () {
      return this.updates.filter(u => u.type === "render").length;
    },
    get totalStateUpdates () {
      return this.updates.filter(u => u.type === "state").length;
    },
  });

  ref.current.updates.push({ type: "render", time: performance.now(), state });

  const recordStateUpdate = useCallback(state => {
    ref.current.updates.push({ type: "state", time: performance.now(), state });
  }, [ref]);

  return [ref.current, recordStateUpdate];
}

function App () {
  const [state, setState] = useState(1);

  const [updateMeta, recordStateUpdate] = useComponentUpdateMeta(state);

  return (<VisualJson data={ updateMeta } />);
}

const reactRoot = ReactDOM.createRoot(document.getElementById("root"));

reactRoot.render(<App />);

</script>

创建未初始化状态,然后在useEffect回调中更新:

const [state, setState] = useState();

useEffect(() => {
  setState(1);
}, []);

在本例中,状态用一个隐式的undefined值初始化,组件被呈现,然后useEffect钩子的回调函数被调用,状态被更新,导致组件第二次呈现:

<style>.json { background-color: hsla(0, 0%, 50%, 0.15); font-family: monospace; font-size: 1rem; padding: 0.5rem; }</style>
<div id="root"></div><script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.development.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.development.js"></script><script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.20.12/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

const { useCallback, useEffect, useRef, useState } = React;

function VisualJson ({ data }) {
  const json = JSON.stringify(
    data,
    // undefined doesn't serialize, so null is used as a substitute:
    (_key, value) => typeof value === "undefined" ? null : value,
    2,
  );
  return (<pre className="json"><code>{ json }</code></pre>);
}

function useComponentUpdateMeta (state) {
  const ref = useRef({
    updates: [],
    get totalRenders () {
      return this.updates.filter(u => u.type === "render").length;
    },
    get totalStateUpdates () {
      return this.updates.filter(u => u.type === "state").length;
    },
  });

  ref.current.updates.push({ type: "render", time: performance.now(), state });

  const recordStateUpdate = useCallback(state => {
    ref.current.updates.push({ type: "state", time: performance.now(), state });
  }, [ref]);

  return [ref.current, recordStateUpdate];
}

function App () {
  const [state, setState] = useState();

  const [updateMeta, recordStateUpdate] = useComponentUpdateMeta(state);

  useEffect(() => {
    setState(1);
    recordStateUpdate(1);
  }, []);

  return (<VisualJson data={ updateMeta } />);
}

const reactRoot = ReactDOM.createRoot(document.getElementById("root"));

reactRoot.render(<App />);

</script>

创建用惰性调用函数初始化的状态:

const [state, setState] = useState(() => 1);

与第一个示例类似:在这种情况下,状态被初始化一次(使用一个返回值成为初始状态值的函数),并且永远不会更新。组件被呈现一次:

<style>.json { background-color: hsla(0, 0%, 50%, 0.15); font-family: monospace; font-size: 1rem; padding: 0.5rem; }</style>
<div id="root"></div><script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.development.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.development.js"></script><script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.20.12/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

const { useCallback, useRef, useState } = React;

function VisualJson ({ data }) {
  const json = JSON.stringify(
    data,
    // undefined doesn't serialize, so null is used as a substitute:
    (_key, value) => typeof value === "undefined" ? null : value,
    2,
  );
  return (<pre className="json"><code>{ json }</code></pre>);
}

function useComponentUpdateMeta (state) {
  const ref = useRef({
    updates: [],
    get totalRenders () {
      return this.updates.filter(u => u.type === "render").length;
    },
    get totalStateUpdates () {
      return this.updates.filter(u => u.type === "state").length;
    },
  });

  ref.current.updates.push({ type: "render", time: performance.now(), state });

  const recordStateUpdate = useCallback(state => {
    ref.current.updates.push({ type: "state", time: performance.now(), state });
  }, [ref]);

  return [ref.current, recordStateUpdate];
}

function App () {
  const [state, setState] = useState(() => 1);

  const [updateMeta, recordStateUpdate] = useComponentUpdateMeta(state);

  return (<VisualJson data={ updateMeta } />);
}

const reactRoot = ReactDOM.createRoot(document.getElementById("root"));

reactRoot.render(<App />);

</script>

还有一点需要考虑:在开发中使用Strict Mode时,React将卸载并重新装载整个应用的节点树,从而有效地导致额外的渲染。
参考文献:

<style>.json { background-color: hsla(0, 0%, 50%, 0.15); font-family: monospace; font-size: 1rem; padding: 0.5rem; }</style>
<div id="root"></div><script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.development.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.development.js"></script><script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.20.12/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

const { StrictMode, useCallback, useEffect, useRef, useState } = React;

function VisualJson ({ data }) {
  const json = JSON.stringify(
    data,
    // undefined doesn't serialize, so null is used as a substitute:
    (_key, value) => typeof value === "undefined" ? null : value,
    2,
  );
  return (<pre className="json"><code>{ json }</code></pre>);
}

function useComponentUpdateMeta (state) {
  const ref = useRef({
    updates: [],
    get totalRenders () {
      return this.updates.filter(u => u.type === "render").length;
    },
    get totalStateUpdates () {
      return this.updates.filter(u => u.type === "state").length;
    },
  });

  ref.current.updates.push({ type: "render", time: performance.now(), state });

  const recordStateUpdate = useCallback(state => {
    ref.current.updates.push({ type: "state", time: performance.now(), state });
  }, [ref]);

  return [ref.current, recordStateUpdate];
}

function App () {
  const [state, setState] = useState();
  const [updateMeta, recordStateUpdate] = useComponentUpdateMeta(state);

  useEffect(() => {
    setState(1);
    recordStateUpdate(1);
  }, []);

  return (<VisualJson data={ updateMeta } />);
}

const reactRoot = ReactDOM.createRoot(document.getElementById("root"));

reactRoot.render(
  <StrictMode>
    <App />
  </StrictMode>
);

</script>

相关问题