React Hooks: useEffect() is called twice even if an empty array is used as an argument

前端 未结 2 869
既然无缘
既然无缘 2021-01-04 02:20


I am new to reactJS and am writing code so that before the data is loaded from DB, it will show loading message, and then after it is loaded, render components with the

相关标签:
2条回答
  • 2021-01-04 02:57

    Not sure why you won't put the result in state, here is an example that calls the effect once so you must have done something in code not posted that makes it render again:

    const App = () => {
      const [isLoading, setLoad] = React.useState(true)
      const [data, setData] = React.useState([])
      React.useEffect(() => {
        console.log('in effect')
        fetch('https://jsonplaceholder.typicode.com/todos')
          .then(result => result.json())
          .then(data => {
            setLoad(false)//causes re render
            setData(data)//causes re render
          })
      },[])
      //first log in console, effect happens after render
      console.log('rendering:', data.length, isLoading)
      return <pre>{JSON.stringify(data, undefined, 2)}</pre>
    }
    
    //render app
    ReactDOM.render(<App />, document.getElementById('root'))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    <div id="root"></div>

    To prevent the extra render you can combine data and loading in one state:

    const useIsMounted = () => {
      const isMounted = React.useRef(false);
      React.useEffect(() => {
        isMounted.current = true;
        return () => isMounted.current = false;
      }, []);
      return isMounted;
    };
    
    
    const App = () => {
      const [result, setResult] = React.useState({
        loading: true,
        data: []
      })
      const isMounted = useIsMounted();
      React.useEffect(() => {
        console.log('in effect')
        fetch('https://jsonplaceholder.typicode.com/todos')
          .then(result => result.json())
          .then(data => {
            //before setting state in async function you should
            //  alsways check if the component is still mounted or
            //  react will spit out warnings
            isMounted.current && setResult({ loading: false, data })
          })
      },[isMounted])
      console.log(
        'rendering:',
        result.data.length,
        result.loading
      )
      return (
        <pre>{JSON.stringify(result.data, undefined, 2)}</pre>
      )
    }
    
    //render app
    ReactDOM.render(<App />, document.getElementById('root'))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    <div id="root"></div>

    0 讨论(0)
  • 2021-01-04 03:00

    Put the console.log inside the useEffect

    Probably you have other side effects that cause the component to rerender but the useEffect itself will only be called once. You can see this for sure with the following code.

    useEffect(()=>{
          /*
          Query logic
          */
          console.log('i fire once');
    },[]);
    

    If the log "i fire once" is triggered more than once it means your issue is one of 2 things.

    This component appears more than once in your page

    This one should be obvious, your component is in the page a couple of times and each one will mount and run the useEffect

    Something higher up the tree is unmounting and remounting

    The component is being forced to unmount and remount on its initial render. This could be something like a "key" change happening higher up the tree. you need to go up each level with this useEffect until it renders only once. then you should be able to find the cause or the remount.

    0 讨论(0)
提交回复
热议问题