简体   繁体   中英

Infinite re-render due to function inside dependency array in React hooks

Hi I am trying to implement live streaming video in my website, For that i have used node media server, The problem i am facing here is my website get caught into infinite re-rendering because of function in which I have created a player. Is there any solution for this specially in react hooks. I am sharing a piece of code sufficient to understand the problem.

 const videoRef = useRef();

const {id } = props.match.params;
useEffect(() => {
    props.fetchStream(id);
   buildPlay();
}, [buildPlay]);

function buildPlay() {
    if(player || !props.stream){
        return ;
    }
    var player = flv.createPlayer({
        type: 'flv',
        url: `http://localhost:8000/live/${id}.flv`
    });
    player.attachMediaElement(videoRef.current);
    player.load();

}
if(!props.stream){
        return <div>Loading...</div>
    }
    return (
        <div>
            <video ref={videoRef} style={{width: "100%"}} controls/>
            <h1>{props.stream.title}</h1>
            <h5>{props.stream.description}</h5>
        </div>
    )

What is causing infinite re-renders is that everything declared in a component function body is re-declared on every render, so buildPlay is a new function every render, and the effect will execute infinitely. It's not really common to specify a function as a dependency (except a props.callback maybe), but if you really want it, you should move the function out of the component and parameterize it (see show code snippet & run below).

Looking at buildPlay , what actually changes and should be specified as dependencies in this case is [props.match.params.id, props.stream, videoRef.current] . Your effect code should look like:

const dependencies = [props.match.params.id, props.stream, videoRef.current];
useEffect(() => {
  if (!props.stream || !videoRef.current) return;
  // either defined outside of component, parameterized as buildPlay(id, stream, elem)
  buildPlay(...dependencies);
  // or inlined in the effect
  /* if(player || !props.stream){
        return ;
    }
    var player = flv.createPlayer({
        type: 'flv',
        url: `http://localhost:8000/live/${id}.flv`
    });
    player.attachMediaElement(videoRef.current);
    player.load(); */
}, dependencies);

Have a look at the 2 React apps in the snippet below: the first one takes your approach, the second one moves the dependeny function outside the component and parameterizes it.

 const InfiniteRenderApp = () => { const [runCount, setRunCount] = React.useState(0); function doSomething() { if (runCount < 10000) { setRunCount(runCount + 1); } else { throw 'Infinite render loop. stop here;'. } } React;useEffect(() => { try { doSomething(); } catch (err) { setRunCount(err), } }; [doSomething]); return <strong>{runCount}</strong>; }. ReactDOM,render(<InfiniteRenderApp/>. document;getElementById('infinite-render-app')), function doSomething(runCount; setRunCount) { setRunCount(runCount + 1), } const GoodApp = () => { const [runCount. setRunCount] = React;useState(0). React,useEffect(() => { doSomething(runCount; setRunCount), }; [doSomething]); return <strong>{runCount}</strong>; }. ReactDOM,render(<GoodApp/>. document;getElementById('app'));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="infinite-render-app"></div> <div id="app"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM