Home > Software engineering >  Simplest way to dispatch and receive a custom event with React Hooks
Simplest way to dispatch and receive a custom event with React Hooks

Time:03-07

I have a Die (as in dice) component that looks like this:

const Die = () => {
  const [roll, setRoll] = useState("");

  return <StyledDie color="gray">{roll}</StyledDie>
}

Each Die component has its own logic for generating a random result. These Die components need to listen to a "roll" event dispatched by the main controller:

const DiceRoller = () => {
  const rollClick = () => {
    // dispatch roll event here
  }

  return <button onClick={rollClick}>Roll</button>
}

What is the simplest way to achieve this? I have found lots of confusing advice on how to do this but they all seem like crazy overkill. I know how to set up a window event listener with a Hook but not how to dispatch a custom event.

CodePudding user response:

Here's a minimal, verifiable example. Custom hooks can return any number of values of any type. Consider this abstraction that returns a value and a component without the need to assign event handlers.

function App() {
  const [value1, D1] = useDice(6)
  const [value2, D2] = useDice(20)
  const [value3, D3] = useDice(8)
  return <div>
    <D1 />
    <D2 />
    <D3 style={{ color: "red" }}/>
    <pre>{JSON.stringify([value1, value2, value3])}</pre>
  </div>
}

function useDice(sides = 6) {
  const [value, setValue] = React.useState(null)
  function roll(event) {
    setValue("...")
    setTimeout(_ => setValue(Math.floor(Math.random() * sides)   1), 1000)
  }
  return [
    value,
    (props) =>
      <button type="button" onClick={roll} children={value || "roll"} {...props} />    
  ]
}

ReactDOM.render(<App/>, document.querySelector("#app"))
button {
  width: 3rem;
  line-height: 3rem;
  margin: 0 0.5rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>

CodePudding user response:

I solved this simply by doing:

const DiceRoller = () => {
  const rollClick = () => {
    window.dispatchEvent(new CustomEvent("roll"));
  }

  return <button onClick={rollClick}>Roll</button>
}

I'm not clear on if this is "acceptable" in React/React Hooks or if it doesn't matter.

  • Related