使用React Hooks进行React-Native制作setInterval声明

时间:2019-05-14 13:18:29

标签: reactjs react-native react-hooks

我和React Hooks玩了几个小时,我可能遇到了一个有趣的问题:使用setInterval不能按我期望的对react-native的操作

function Counter() {
  const [time, setTime] = useState(0);
  const r = useRef(null);
  r.current = { time, setTime };
  useEffect(() => {
    const id = setInterval(() => {
      console.log("called");
      r.current.setTime(r.current.time + 1);
    }, 1000);
    return () => {
      console.log("cleared");
      clearInterval(id);
    };
  }, [time]);

  return <Text>{time}</Text>;
}

每次time状态更改时,以上代码应清除clearInterval 它在ReactJS上工作正常,但在React-native上却出现错误,提示"Callback() it's not a function"

enter image description here

它在Reactjs中按预期工作

https://codesandbox.io/s/z69z66kjyx

"dependencies": {
    "react": "16.8.3",
    "react-native": "^0.59.6",
    ...}

更新: 我尝试像这样使用ref,但是仍然出现相同的错误

const [time, setTime] = useState(0);
        useInterval(() => {
            setTime(time +1);
        });

        return (<Text>{time}</Text>);
}

function useInterval(callback) {
    const savedCallback = useRef();

    // Remember the latest function.
    useEffect(() => {
        savedCallback.current = callback;
    }, [callback]);

    // Set up the interval.
    useEffect(() => {
            let id = setInterval(()=>savedCallback.current(), delay);
            return () => clearInterval(id);
    });
}

2 个答案:

答案 0 :(得分:2)

因为要通过DOM节点引用对DOM进行突变,并且DOM突变将在渲染的时间和效果对其进行更改之间更改DOM节点的外观。那么您将不需要使用useEffect,而将要使用useLayoutEffect

useLayoutEffect在React执行所有DOM突变后立即同步运行。

import React, {useState, useLayoutEffect,useRef} from 'react';
import { Text} from 'react-native';

const [time, setTime] = useState(0);
        useInterval(() => {
            setTime(time +1);
        });

        return (<Text>{time}</Text>);
}

function useInterval(callback) {
    const savedCallback = useRef();

    // Remember the latest function.
    useLayoutEffect(() => {
        savedCallback.current = callback;
    }, [callback]);

    // Set up the interval.
    useLayoutEffect(() => {
            let id = setInterval(()=>{
              console.log('called');
              return savedCallback.current();
            }, delay);
            return () => {
                console.log('cleared');
                return clearInterval(id);
            }
    });
}

如果您仅使用useEffect并收到此错误

  

Uncaught TypeError: callback is not a function at flushFirstCallback (scheduler.development.js:348) at flushWork (scheduler.development.js:441) at MessagePort.channel.port1.onmessage (scheduler.development.js:188)

这是RN中的一个错误,因为版本scheduler错误,不幸的是RN并没有明确依赖雾化器的调度程序版本。 Dan Abramov 已在调度程序版本"0.14.0"

上修复了此错误

要解决此问题,只需运行以下命令

npm install scheduler@0.14.0 --save

或者尝试将"scheduler": "0.14.0"添加到package.json中的dependencies中,然后重新运行包管理器

答案 1 :(得分:0)

您应该仍然可以在效果钩子中使用状态钩子变量,因为它们在范围内。

useRef:此处不会跟踪突变,因此不会触发重新渲染。

CodeSandbox Counter Example

我觉得以引用方式使用refs比直接使用state和setter更为冗长。 useRef引用用于一段时间内的可变值,但您已经通过useState钩子获得了。 ref之所以有效,是因为您并没有真正使ref发生变化,而是在每个渲染周期中仅使用已更新 useState钩子的内容覆盖它。

我已经更新了沙箱,将useRef用作您的方式,您的useEffect钩子导致您的清理函数在每个渲染器上启动,因此删除了相关性。您会注意到,直到刷新为止,您只会看到“被叫”。