在React的官方文档中提到了-

如果你熟悉React类的生命周期方法,你可以思考 将useEffect钩子作为componentDidMount, componentDidUpdate和 componentWillUnmount总和。

我的问题是-我们如何在钩子中使用componentWillMount()生命周期方法?


当前回答

useComponentWillMount钩

const useComponentWillMount = (cb) => {
    const willMount = useRef(true)

    if (willMount.current) cb()

    willMount.current = false
}

当出现顺序问题(比如在另一个脚本之前运行)时,这个钩子可以作为一个保护程序。如果不是这样,请使用useComnponentDidMount,它更符合React钩子的范例。

useComponentDidMount钩

const useComponentDidMount = cb => useEffect(cb, []);

如果你知道你的效果应该只运行一次在开始使用这个解决方案。它只会在组件挂载后运行一次。

useEffect范式

类组件具有生命周期方法,这些方法定义为组件时间轴上的点。钩子不遵循这种范式。相反,效果应该由内容构成。

function Post({postID}){
  const [post, setPost] = useState({})

  useEffect(()=>{
    fetchPosts(postID).then(
      (postObject) => setPost(postObject)
    )
  }, [postID])

  ...
}

在上面的例子中,效果处理的是获取文章的内容。而不是一个特定的时间点,它有一个它所依赖的值- postID。每次postID得到一个新值(包括初始化),它都会重新运行。

组件将挂载讨论

在类组件中,componentWillMount被认为是遗留的(源1,源2)。它是遗留的,因为它可能运行不止一次,而且还有另一种选择——使用构造函数。这些考虑因素与功能组件无关。

其他回答

对于大多数人来说,这可能很清楚,但请记住,在函数组件体内调用的函数充当beforeRender。这并没有回答在ComponentWillMount上运行代码的问题(在第一次渲染之前),但由于它是相关的,可能会帮助其他人,所以我把它留在这里。

const MyComponent = () => {
  const [counter, setCounter] = useState(0)
  
  useEffect(() => {
    console.log('after render')
  })

  const iterate = () => {
    setCounter(prevCounter => prevCounter+1)
  }

  const beforeRender = () => {
    console.log('before render')
  }

  beforeRender()

  return (
    <div>
      <div>{counter}</div>
      <button onClick={iterate}>Re-render</button>
    </div>
  )
}

export default MyComponent

https://reactjs.org/docs/hooks-reference.html#usememo

记住,传递给useMemo的函数在呈现期间运行。 不要在那里做渲染时通常不会做的事情。 例如,副作用属于useEffect,而不是useMemo。

componentWillMount已弃用(正如在其他评论中提到的),原因是我认为它很容易被一个简单的HOC处理。

const withComponentWillMount = (WrappedComponent, handler) => {
  return (props) => {
    return handler(props) ? <WrappedComponent {...props} /> : null;
  }
}

我通常在我的项目中实现这个解决方案。使用此HOC,如果处理程序返回false,则组件内部不运行任何内容,包括钩子。

So for React hooks, I think declaring your logic before the return statement can work. You should have a state that is set to true by default. In my case, I called the state componentWillMount. Then a conditional to run a block of code when this state is true (the block of code contains the logic you want executed in your componentWillMount), the last statement in this block should be resetting the componentWillMountState to false (This step is important because if it is not done, infinite rendering will occur) Example

// do your imports here

const App = () =>  {
  useEffect(() => {
    console.log('component did mount')
  }, [])
  const [count, setCount] = useState(0);
  const [componentWillMount, setComponentWillMount] = useState(true);
  if (componentWillMount) {
    console.log('component will mount')
    // the logic you want in the componentWillMount lifecycle
    setComponentWillMount(false)
  }
  
  return (
    <div>
      <div>
      <button onClick={() => setCount(count + 1)}> 
        press me
      </button>
      <p>
        {count}
      </p>
      
      </div>
    </div>
  )
}

考虑到

componentWillMount已弃用(1,2,3),建议的替换是在构造函数中执行代码 在函数组件的return语句之前执行的代码在呈现之前隐式运行 与挂载类组件大致相当的是函数组件的初始调用 目标是在UI更新之前执行一些代码一次

解决办法是

在函数组件的主体中只运行一次函数。这可以通过useState、useMemo或useEffect来实现,具体取决于用例所需的时间。

由于代码需要在将初始呈现提交给屏幕之前运行,这将取消useEffect的资格,因为“传递给useEffect的函数将在将呈现提交给屏幕之后运行。”4。

因为我们想要保证代码只运行一次,这就使useMemo失去了资格,因为“在未来,React可能会选择“忘记”一些以前记住的值,并在下一次渲染时重新计算它们”5。

useState支持惰性初始状态计算,保证在初始渲染期间只运行一次,这似乎是一个很好的工作候选人。

useState的示例:

const runOnceBeforeRender = () => {};

const Component = () => {
  useState(runOnceBeforeRender);

  return (<></>);
}

作为自定义钩子:

const runOnceBeforeRender = () => {};

const useOnInitialRender = (fn) => {
  useState(fn);
}

const Component = () => {
  useOnInitialRender(runOnceBeforeRender);

  return (<></>);
};

runOnceBeforeRender函数可以选择返回一个在函数第一次呈现时立即可用的状态,不会触发重新呈现。

一个(可能不必要的)NPM包:useOnce钩子