简体   繁体   English

如何避免useEffect钩子中的setState导致第二次渲染

[英]How to avoid setState in useEffect hook causing second render

I'm writing a custom hook that takes an id as input and then should do the following tasks: 我正在编写一个自定义钩子,它将id作为输入,然后应该执行以下任务:

  • get data for that id synchronously from a store 从商店同步获取该ID的数据
  • subscribe to changes in this store and update data accordingly 订阅此商店中的更改并相应地更新数据

I came up with the following implementation which has the downside that I am setting state directly from inside the effect hook, causing a second render. 我想出了以下实现,它的缺点是我直接从效果挂钩内部设置状态,导致第二次渲染。

function useData({id}) {
    // set initial data on first render
    const [data, setData] = useState(Store.getData(id));

    useEffect(() => {
        // when id changes, set data to whatever is in store at the moment
        // here we call setData directly from inside useEffect, causing a second render
        setData(Store.getData(id));
        // subscribe to changes to update data whenever it changes in the store
        return Store.onChange(id, setData);
    }, [id]);

    return data;
}

A second approach I tried was to add a dummy state that is only there to cause a re-render. 我尝试的第二种方法是添加一个仅用于重新渲染的虚拟状态。 In this approach I am directly returning the data received from Store.getData(). 在这种方法中,我直接返回从Store.getData()接收的数据。 This ensures that I will get the freshest data with every render and the useEffect ensures that every onChange trigger will cause a new render. 这确保了我将获得每个渲染的最新数据,并且useEffect确保每个onChange触发器将导致新的渲染。

function useData({id}) {
    // adding some dummy state that we only use to force a render
    const [, setDummy] = useState({});
    const refresh = useCallback(() => {
        setDummy({});
    }, []);

    useEffect(() => {
        // subscribe to changes cause a refresh on every change
        return Store.onChange(id, refresh);
    }, [id, refresh]);

    return Store.getData[id];
}

The second approach works well but it feels weird to add this dummy state. 第二种方法效果很好但添加这种虚拟状态感觉很奇怪。 Sure, I could put this into another useRefresh hook but I am not sure if this would really be a good practice. 当然,我可以把它放到另一个useRefresh挂钩但我不确定这是否真的是一个好习惯。

Is there any better way of implementing this, without calling setData directly from inside useEffect and without relying on some unused dummy state? 有没有更好的方法来实现这一点,而无需直接从useEffect内部调用setData而不依赖于一些未使用的虚拟状态?

So by now you use useState inside your hook just to re-trigger rendering of host component once store is changed. 所以现在你在钩子里面使用useState只是为了在更改存储后重新触发主机组件的渲染。 How about taking change handler from the outside? 从外面接受变更处理程序怎么样?

function useData(id, onChanged) {
  useEffect(() => {
    return store.onChange(id, onChanged);
  }, [id, onChanged]);

 return store.getData(id);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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