如何在React Hooks中使用componentWillMount()?

时间:2018-11-25 04:13:09

标签: javascript reactjs jsx react-hooks

在React的官方文档中提到-

  

如果您熟悉React类的生命周期方法,可以考虑   useEffect Hook作为componentDidMount,componentDidUpdate和   componentWillUnmount组合。

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

18 个答案:

答案 0 :(得分:57)

您不能在钩子中使用任何现有的生命周期方法(componentDidMount,componentDidUpdate,componentWillUnmount等)。它们只能在类组件中使用。和挂钩只能在功能组件中使用。下面来自React文档

  

如果您熟悉React类的生命周期方法,则可以将useEffect Hook看作componentDidMount,componentDidUpdate和componentWillUnmount的组合。

建议是,您可以从功能组件中的类组件模仿这些生命周期方法。

componentDidMount 中的

代码在安装组件时运行一次。此行为的useEffect钩子等效值为

useEffect(() => {
  // Your code here
}, []);

在此处注意第二个参数(空数组)。这将只运行一次。

没有第二个参数,将在组件的每次渲染上调用useEffect挂钩,这可能很危险。

useEffect(() => {
  // Your code here
});

componentWillUnmount用于清理(例如删除事件列表器,取消计时器等)。假设您要在componentDidMount中添加一个事件列表器,然后在componentWillUnmount中将其删除,如下所示。

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}

与上述代码等效的钩子如下

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])

答案 1 :(得分:14)

这是我使用useRef钩子在功能组件中模拟构造函数的方式:

function Component(props) {
    const willMount = useRef(true);
    if (willMount.current) {
        console.log('This runs only once before rendering the component.');
        willMount.current = false;        
    }

    return (<h1>Meow world!</h1>);
}

这是生命周期示例:

function RenderLog(props) {
    console.log('Render log: ' + props.children);
    return (<>{props.children}</>);
}

function Component(props) {

    console.log('Body');
    const [count, setCount] = useState(0);
    const willMount = useRef(true);

    if (willMount.current) {
        console.log('First time load (it runs only once)');
        setCount(2);
        willMount.current = false;
    } else {
        console.log('Repeated load');
    }

    useEffect(() => {
        console.log('Component did mount (it runs only once)');
        return () => console.log('Component will unmount');
    }, []);

    useEffect(() => {
        console.log('Component did update');
    });

    useEffect(() => {
        console.log('Component will receive props');
    }, [count]);


    return (
        <>
        <h1>{count}</h1>
        <RenderLog>{count}</RenderLog>
        </>
    );
}
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props

当然,类组件没有Body步骤,由于函数和类的概念不同,不可能进行1:1仿真。

答案 2 :(得分:10)

您可以破解useMemo挂钩来模仿componentWillMount生命周期事件。 只要做:

const Component = () => {
   useMemo(() => {
     // componentWillMount events
   },[]);
   useEffect(() => {
     // componentDidMount events
     return () => {
       // componentWillUnmount events
     }
   }, []);
};

在与状态交互的任何内容之前,您都需要保留useMemo挂钩。这不是它的预期目的,但是它对所有componentWillMount问题都有效。

之所以可行,是因为useMemo不需要实际返回值,并且您不必实际将其用作任何值,但是由于它基于仅运行一次(“ []”)的依赖关系来存储一个值,因此它会位于我们的组件在其他组件挂载之前会运行一次。

答案 3 :(得分:9)

在钩子中响应生命周期方法

为了简单的视觉参考,请跟随这张图片

enter image description here

如上图所示,对于ComponentWillUnmount,你必须这样做

 useEffect(() => {
    return () => {
        console.log('componentWillUnmount');
    };
   }, []);

答案 4 :(得分:8)

根据reactjs.org,将来将不再支持componentWillMount。 https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

无需使用componentWillMount。

如果要在组件安装之前做一些事情,只需在Constructor()中做。

如果要发出网络请求,请不要在componentWillMount中进行。这是因为这样做会导致意外的错误。

网络请求可以在componentDidMount中完成。

希望有帮助。


更新于08/03/2019

要求componentWillMount的原因可能是因为您想在渲染之前初始化状态。

只需在useState中完成。

        const helloWorld=()=>{
           const [value,setValue]=useState(0) //initialize your state here
            return <p>{value}</p>
            }
         export default helloWorld;

或者,例如,如果原始代码如下所示,您可能想在componentWillMount中运行一个函数:

  componentWillMount(){
    console.log('componentWillMount')
    }

使用钩子,您需要做的就是删除生命周期方法:

    const hookComponent=()=>{
       console.log('componentWillMount')
       return <p>you have transfered componeWillMount from class component into hook </p>
    }

我只想在有关useState的第一个答案中添加一些内容

useEffect(()=>{})

useEffect在每个渲染器上运行,它是componentDidUpdate,componentDidMount和ComponentWillUnmount的组合。

 useEffect(()=>{},[])

如果我们在useEffect中添加一个空数组,它将在组件安装时运行。这是因为useEffect将比较传递给它的数组。 因此它不必是一个空数组,它可以是不变的数组。例如,它可以是[1,2,3]或['1,2']。 useEffect仍然仅在安装组件时运行。

这取决于您是希望它只运行一次还是在每次渲染后运行。只要您知道自己在做什么,就忘记添加数组就没有危险。

我创建了一个钩子样本。请检查一下。

https://codesandbox.io/s/kw6xj153wr

答案 5 :(得分:3)

如果

useLayoutEffect的功能实际上与[]类似,则可以使用一组空的观察者(componentWillMount)来完成此操作-该功能将在第一个内容到达DOM之前运行- -尽管实际上有两个更新,但是在绘制到屏幕之前它们是同步的。

例如:


function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (<div>some content</div>);
}

与初始化器/设置器或useState相比,useEffect的好处是尽管它可以计算渲染过程,但用户不会注意到对DOM的实际重新渲染,这是在第一个引人注意的渲染器之前运行 before useEffect则不是这样。缺点当然是您的第一次渲染会稍有延迟,因为在绘画到屏幕之前必须进行检查/更新。不过,这确实取决于您的用例。

我个人认为,useMemo在某些利基情况下需要做一些繁重的工作-只要记住,这是例外而不是常规。

答案 6 :(得分:1)

使用componentDidMount实现componentWillUnmountuseEffect有很好的解决方法。

根据文档,useEffect可以返回“清理”功能。此函数不会在第一个useEffect调用中被调用,仅在后续调用中被调用。

因此,如果我们使用完全没有依赖项的useEffect钩子,则仅在安装组件时才调用该钩子,而在卸载该组件时才调用“ cleanup”函数。

useEffect(() => {
    console.log('componentDidMount');

    return () => {
        console.log('componentWillUnmount');
    };
}, []);

仅当卸载组件时才调用清除返回函数调用。

希望这会有所帮助。

答案 7 :(得分:1)

鉴于

  • componentWillMount 已弃用(123),并且建议的替换正在执行 constructor
  • 中的代码
  • 在函数组件的 return 语句之前执行的代码在呈现之前隐式运行
  • 大致相当于挂载类组件是对功能组件的初始调用
  • 目标是在更新 ui 之前执行一次代码

解决方案是

在功能组件的主体中只运行一次函数。这可以通过 useStateuseMemouseEffect 实现,具体取决于用例所需的时间。

由于代码需要在初始渲染提交到屏幕之前运行,因此 useEffect 不合格,因为“传递给 useEffect 的函数将在渲染提交到屏幕后运行。” 4

因为我们想保证代码只运行一次,所以这不符合 useMemo,因为“在未来,React 可能会选择‘忘​​记’一些之前记忆的值并在下一次渲染时重新计算它们”{{ 3}}。

useState 支持 5 计算,这些计算保证在初始渲染期间只运行一次,这似乎是这项工作的不错选择。

useState 示例:

const runOnceBeforeRender = () => {};

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

  return (<></>);
}

作为自定义钩子:

const runOnceBeforeRender = () => {};

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

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

  return (<></>);
};

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

答案 8 :(得分:0)

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

  

请记住,传递给useMemo的函数在渲染期间运行。   在该处不要执行渲染时通常不会执行的任何操作。   例如,副作用属于useEffect,而不是useMemo。

答案 9 :(得分:0)

useComponentDidMount挂钩

在大多数情况下,useComponentDidMount是要使用的工具。组件安装(初始渲染)后,它将仅运行一次。

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

useComponentWillMount

重要的是要注意,在类组件componentWillMount中被认为是遗留的。如果您需要代码在组件安装之前仅运行一次,则可以使用构造函数。 More about it here

由于功能组件并不等同于构造函数,因此在某些情况下,使用钩子在组件挂载之前仅运行一次代码可能是有意义的。 useComponentWillMount仅在第一次从组件中调用后才会立即运行。但是,不要传递包含副作用的回调(例如服务器调用),因为我们不想延迟组件的渲染。这些应该传递给useComponentDidMount

const useComponentWillMount = func => {
  const [willMount, setWillMount] = useState(true);
  useComponentDidMount(() => setWillMount(false));
  if (willMount) {
    func();
  }
};

演示

const Component = (props) => {
  useComponentWillMount(() => console.log("Runs only once before component mounts"));
  useComponentDidMount(() => console.log("Runs only once after component mounts"));
  ...
  return (
    <div>
      {...}
    </div>
  );
}

Full Demo

答案 10 :(得分:0)

我写了一个自定义钩子,它将在第一次渲染之前运行一次函数。

useBeforeFirstRender.js

@types/node

用法:

import { useState, useEffect } from 'react'

export default (fun) => {
  const [hasRendered, setHasRendered] = useState(false)

  useEffect(() => setHasRendered(true), [hasRendered])

  if (!hasRendered) {
    fun()
  }
}

答案 11 :(得分:0)

本·卡尔普(Ben Carp)的回答对我来说似乎只有一个有效的答案。

但是由于我们正在使用功能性方法,因此只有另一种方法可以从闭包和HoC中受益:

const InjectWillmount = function(Node, willMountCallback) {
  let isCalled = true;
  return function() {
    if (isCalled) {
      willMountCallback();
      isCalled = false;
    }
    return Node;
  };
};

然后使用它:

const YourNewComponent = InjectWillmount(<YourComponent />, () => {
  console.log("your pre-mount logic here");
});

答案 12 :(得分:0)

您的原始问题的简短答案,如何将componentWillMount与React Hooks一起使用:

componentWillMountdeprecated and considered legacy。反应recommendation

通常,我们建议使用constructor()来初始化状态。

现在您在Hook FAQ中发现,功能组件的类构造函数等效于:

构造函数:功能组件不需要构造函数。您可以在useState调用中初始化状态。如果计算初始状态的成本很高,则可以将一个函数传递给useState。

因此componentWillMount的用法示例如下:

const MyComp = () => {
  const [state, setState] = useState(42) // set initial value directly in useState 
  const [state2, setState2] = useState(createInitVal) // call complex computation

  return <div>{state},{state2}</div>
};

const createInitVal = () => { /* ... complex computation or other logic */ return 42; };

答案 13 :(得分:0)

正如react document中所述:

<块引用>

您可能认为我们需要一个单独的效果来执行 清理。但是添加和删除订阅的代码是如此紧密 相关的是 useEffect 旨在将它保持在一起。如果你的效果 返回一个函数,React 会在需要清理时运行它:

useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // Specify how to clean up after this effect:
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

所以我们需要在钩子中使用 componentWillUnmount 的唯一事情就是在 useEffect 中返回一个函数,如上所述。

答案 14 :(得分:0)

所以对于 React 钩子,我认为在 return 语句可以工作之前声明你的逻辑。您应该有一个默认设置为 true 的状态。就我而言,我调用了状态 componentWillMount。然后有条件地在此状态为真时运行代码块(代码块包含您要在 componentWillMount 中执行的逻辑),此块中的最后一条语句应将 componentWillMountState 重置为 false(这一步很重要,因为如果它没有完成,会发生无限渲染) 示例

// 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>
  )
}

答案 15 :(得分:0)

这可能不是 componentWillMount 方法的确切替代方法,但这里有一个方法可以用来实现相同的目标,但使用 useEffect :

首先将您检索数据的对象初始化为空值并定义 useEffect 方法:

const [details, setDetails] = useState("")

  useEffect(() => {
    retrieveData(); 
  }, []);

const retrieveData = () => {       
       getData()                  // get data from the server 
      .then(response => {
        console.log(response.data);
        setDetails(response.data)
      })
      .catch(e => {
        console.log(e);
      })
  }

现在在我们返回的 JSX 中添加一个三元运算符

*return(
  <div>
    { 
   details ? (
   <div class="">
   <p>add Your Jsx Here</p>
</div>
): (
  <div>
<h4>Content is still Loading.....</h4>
</div>
)
}
  </div>
)*

这将确保在对象 'details' 中有数据之前,三元运算符的第二部分被加载,这反过来会触发 useEffect 方法,该方法导致设置从服务器接收到的数据到 'details' 对象中,因此主 JSX 的渲染

答案 16 :(得分:-1)

对于大多数人来说可能很清楚,但请记住,在功能组件主体内部调用的功能充当 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

答案 17 :(得分:-3)

只需在useEffect中添加一个空的dependency数组,它将像componentDidMount一样工作。

useEffect(() => {
  // Your code here
  console.log("componentDidMount")
}, []);