如何停止初始渲染以使用Effect挂钩

sq1bmfud  于 2022-10-15  发布在  React
关注(0)|答案(4)|浏览(233)

之前我有一个Class组件,所以我在使用生命周期方法时没有遇到任何问题,但在转换到Use Effect挂钩之后,我面临着我不希望发生的初始呈现问题。

componentDidMount() {
    this.setState({
        patchVal:this.props.patchTaskVal,
        startTime:this.props.patchStartTime,
        setEndTime:this.props.patchEndTime
    })
}

componentDidUpdate(prevProps) {
    if (prevProps.patchTaskVal !== this.props.patchTaskVal) {
        this.callValidation()
    }

    if (prevProps.closeTask !== this.props.closeTask) {
        this.setState({
            showValue:false,
            progressValue:[],
            startTime:new Date(),
            setEndTime:""
        })
    }

    if (prevProps.patchStartTime !== this.props.patchStartTime || prevProps.endTime !== this.props.endTime && this.props.endTime !== "") {
        this.setState({
            startTime:this.props.patchStartTime,
            setEndTime:parseInt(this.props.endTime)
        })
    }
}

功能

const [patchTaskVal, setPatchTaskVal]=useState(/*initial value */) 
const [startTime, setStartTime]=useState()
const [endTime, setEndTime] = useState()

**// I want only this useEffect to run on the initial render**

useEffect(() => {
    setPatchTaskVal(props.patchTaskVal)
    ...//set other states
}, [])

useEffect(() => {
    callValidation() 
}, [props.patchTaskVal])

useEffect(() => {
    //setShowValue...
}, [props.closeTask])

useEffect(() => {
    if (props.endTime != "") {
        // set states...
    }
}, [props.patchStartTime,props.endTime])

在这里,我面临一个问题,其中所有的useEffect都在初始渲染上运行,请为此建议一个解决方案,以便只有第一个useEffect在初始渲染上运行,而所有其他useEffect将根据其依赖属性值运行。

oaxa6hgo

oaxa6hgo1#

你基本上需要一个ref来告诉你这是否是第一次渲染。引用值持续存在,而不是重现器。可以从truthy值开始,并在第一次渲染后将其切换为false(使用带有空数组[]的useEffect)。在此基础上,您可以运行所需的代码。
您还可以将整个代码放在一个定制的钩子中:

import { useEffect, useRef } from "react";

const useOnUpdate = (callback, deps) => {
  const isFirst = useRef(true);
  useEffect(() => {
    if (!isFirst.current) {
      callback();
    }
  }, deps);

  useEffect(() => {
    isFirst.current = false;
  }, []);
};

export default useOnUpdate;

您可以在组件中调用此挂钩,如下所示:

useOnUpdate(() => {
    console.log(prop);
  }, [prop]);

在钩子中:在初始渲染之后,两个useEffect都运行。但当第一个效果运行时,isFirst.current的值为真。因此不会调用回调。第二个useEffect也会运行,并将isFirst.current设置为False。现在在后续只渲染第一次useEffect运行(当依赖项更改时),并且isFirst.current现在为FALSE,因此执行callback
在这里,两个useEffects的顺序非常重要。否则,在具有depsuseEffect中,即使在第一次渲染之后,isFirst.current也将为真。
Link

bsxbgnwa

bsxbgnwa2#

试试这个..。

let init = true;
useEffect( ()=>{
if(init) {

    setPatchTaskVal(props.patchTaskVal)
init = false;
    ...//set other states}
}, [])

useEffect( ()=> {

    !init && callValidation() 
},[props.patchTaskVal])

useEffect( ()=>{
//!init && setShowValue...
},[props.closeTask])

useEffect( ()=>{
    if(props.endTime!="" && !init){
    // set states...
    }
},[props.patchStartTime,props.endTime])
yx2lnoni

yx2lnoni3#

希望我对你的问题的理解是正确的。
为什么不直接添加if statement来检查state不是undefineddefault value

useEffect( ()=> {
  if (props.patchTaskVal) {
    callValidation() 
  }
},[props.patchTaskVal])

useEffect( ()=>{
  if (props.closeTask) {
    //setShowValue...  
  }
},[props.closeTask])

useEffect( ()=>{
    if(props.patchStartTime){
    // set states...
    }

    if(props.endTime){
    // set states...
    }
},[props.patchStartTime,props.endTime]

根据您的类组件,

this.setState({
patchVal:this.props.patchTaskVal,
startTime:this.props.patchStartTime,
setEndTime:this.props.patchEndTime
})

功能组件应该将道具Map到组件的状态。像这样

const [patchTaskVal, setPatchTaskVal]=useState(props.patchTaskVal) 
const [startTime, setStartTime]=useState(props.patchStartTime)
const [endTime, setEndTime] = useState(props.patchEndTime)
62lalag4

62lalag44#

如果将函数组件和类组件进行比较,您会注意到缺少一个部分--前面的道具。
功能组件在范围内没有以前的道具,但您可以用一个小技巧自己保存它们:保存它们以供参考,这样它就不会影响您的渲染周期。
因为现在您有了以前的道具和当前的道具,所以您可以应用与类组件相同的逻辑。

import React, { useRef, useEffect, useState } from "react";

default function App() {
  const [input, setInput] = useState("");
  const [commitInput, setCommitInput] = useState("");

  return (
    <>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
      />
      <button onClick={() => setCommitInput(input)}>apply</button>
      <Child test={commitInput} />
    </>
  );
}

function Child(props) {
  const prev = useRef(props.test);
  useEffect(() => {
    if (prev.current !== props.test) {
      alert("only when changes");
    }
  }, [props.test]);

  return <div>{props.test}</div>;
}

相关问题