DEV Community

loading...

Discussion on: In React, component controls you!

Collapse
link2twenty profile image
Andrew Bone

I might be being a bit dumb but why do you need the event? If you're just interested in passing the changed value you can use useEffect, or am I missing something?

React.useEffect(() => {
  onChange(value);
}, [value, onChange])
Enter fullscreen mode Exit fullscreen mode
Collapse
poeticgeek profile image
Yoav Niran Author

Thats indeed a possibility. If you never need the event object then its OK to just expose the value. However, React's onChange does give you the event. And when writing low level components and trying to be as transparent as possible, we attempt to provide as many options to the consuming code.
It means that the "native" onChange event from React (ex: user types a value directly) will be able to pass the event object. While custom interactions (like the up/down buttons) wont and there will be inconsistency between these types.
This article shows a solution that aims to provide the most transparent and consistent experience for anyone consuming this type of component.

Collapse
link2twenty profile image
Andrew Bone

OK, I think I understand the problem now 😊

I think this custom hook will fix your issues.

import React from "react";

export default function (initalState, target) {
  const [state, setState] = React.useState(initalState);
  const [newState, setNewState] = React.useState(initalState);
  const event = React.useMemo(() => new Event("change", { bubbles: true }), []);

  const updateStates = React.useCallback((updated) => {
    setState(updated);
    setNewState(updated);
  }, []);

  React.useEffect(() => {
    if (state === newState) return;
    const { current } = target;

    if (!current) return;

    current.value = newState;
    const tracker = current._valueTracker;

    if (tracker) {
      tracker.setValue(state);
    }

    current.dispatchEvent(event);
  }, [state, newState, event, target]);

  return [state, updateStates, setNewState];
}
Enter fullscreen mode Exit fullscreen mode

You basically use this hook the same way you use React.useState but you have to pass in a ref target when you initialise it also it returns 3 items rather than 2, [state, setState, callStateChange].

That last function will change the value of the input and trigger an event for your onChange to pick up and deal with.

Here's a working example.

Unless I've totally misunderstood again 😅