1

I need to update a useCallback hook on a certain event that is emitted by eventEmitter3. Right now I update a local state with the current time to trigger the useCallback. That works but looks crazy complicated. Is there a better solution?

const [referencesDidChange, setReferencesDidChange] = useState(0);

useEffect(() => {
    const referencesChange = () => {
        setReferencesDidChange(new Date().getTime());
    };
    eventEmitter.on(Events.ReferencesChange, referencesChange);
    return () => {
        eventEmitter.removeListener(Events.ReferencesChange, referencesChange);
    };
}, []);

const renderLeaf = useCallback(props => <Leaf {...props} />, [referencesDidChange]);
Obiwahn
  • 2,677
  • 2
  • 26
  • 36

2 Answers2

2

I would suggest pulling the most recent values via a ref instead of updating the callback.

In general, with callbacks, you don't need to update them if you pull values at the time of running them. I'm not suggesting that you always do this by default but in certain cases it can clean things up if you rearchitect to pull on invocation if you can.

const someRef = useRef(null);

useEffect(() => {
  someRef.current = someChangingValue;
}, [someChangingValue]);

const renderLeaf = useCallback(() => {
  const latestValues = someRef.current;

  // use latest values…
}, []);
Rico Kahler
  • 17,616
  • 11
  • 59
  • 85
  • my main concern is the `setReferencesDidChange(new Date().getTime());` How can I avoid that with your answer? – Obiwahn Apr 20 '20 at 09:17
  • 1
    What confuses me is why you would need to re-create the `renderLeaf` callback based on this event. To me, it seems like all you need to do is force a re-render. If you need an event to force a re-render (because e.g. reading from non-react state), then doing something like `setReferencesDidChange(new Date().getTime())` is fine. [The react docs have an example of this pattern.](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate) – Rico Kahler Apr 20 '20 at 18:22
  • 1
    If you have more code you're willing to share I could give you a better answer, otherwise, what you're doing is probably fine! – Rico Kahler Apr 20 '20 at 18:27
0

As Rico Kahler pointet out in the comments in his answer, there is a react docs example on this, which results in the much clearer:

const [referencesDidChange, setReferencesDidChange] = useReducer(x => x + 1, 0);

useEffect(() => {
    eventEmitter.on(Events.ReferencesChange, setReferencesDidChange);
    return () => {
        eventEmitter.removeListener(Events.ReferencesChange, setReferencesDidChange);
    };
}, []);

const renderLeaf = useCallback(props => <Leaf {...props} />, [referencesDidChange]);
Obiwahn
  • 2,677
  • 2
  • 26
  • 36