0

Why this alert box is appearing twice in a simple useReducer Application,

Click here for the codesandbox link,

To regenerate the scenario

  1. increase the value of input using up arrow (Press only once)
  2. decrease the value of input by down key (Press only once)

here is the code

import "./styles.css";
import { useReducer } from "react";
const reducerFunction = (state, action) => {
  switch (action.type) {
    case "testing": {
      if (action.payload.value > 0) {
        return { ...state, testing: action.payload.value };
      } else {
        alert("some message");
        return state;
      }
    }
    default: {
      throw new Error("Invalid Action");
    }
  }
};
export default function App() {
  const defaultValue = {
    testing: 0
  };
  const [state, dispatch] = useReducer(reducerFunction, defaultValue);

  return (
    <input
      value={state.testing}
      onChange={(e) => {
        dispatch({ type: "testing", payload: { value: e.target.value } });
      }}
      type="number"
    />
  );
}

skyboyer
  • 22,209
  • 7
  • 57
  • 64

3 Answers3

5

You are using StrictMode which invokes functions passed to useReducer twice, from docs:

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

Functions passed to useState, useMemo, or useReducer

// Remove Strict.Mode
ReactDOM.render(
  <App />,
  rootElement
);
Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
  • It doesn't get invoked twice for every state change, only specifically when input becomes `0`, any ideas why? – T J May 10 '21 at 17:07
  • Yes, he has condition only for `action.payload.value > 0` – Dennis Vash May 10 '21 at 17:10
  • I mean, when exactly does `StrictMode` decide to invoke reducer twice? Why it specifically invokes it for `0`, and not afterwards for -1, -2 etc? – T J May 10 '21 at 17:13
  • thanks ,it did fixed my error and i didnt knew that about the strict mode ,can you please elaborate on the double invoking part in a simpler way or perhaps share some reference ,it would really appreciate the effort, ,although i do appreciate the effort now as well : ) – Shishir Tiwari May 10 '21 at 17:15
  • 1
    @TJ, it does invoked twice, but notice he returning the same state, which won't trigger a render on next change. – Dennis Vash May 10 '21 at 17:20
  • @ShishirTiwari Not sure about elaborating more, the docs are great, you may want to check my other answer as well: https://stackoverflow.com/questions/66148027/how-does-react-strict-mode-works/66149458#66149458 – Dennis Vash May 10 '21 at 17:20
  • @DennisVash not a problem and again thanks ! – Shishir Tiwari May 10 '21 at 17:53
1

Your initial state is this:

const defaultValue = {
  testing: 0
};

Which is bound to the input value, but then from your reducer you're returning:

return { inputValue: action.payload.value };

Causing testing to be undefined.

You can see this error in console:

Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components

Your code should be

return {...state, testing: action.payload.value };

This will show one alert and prevent negative values

Edit reactAlertCheck (forked)

T J
  • 42,762
  • 13
  • 83
  • 138
  • @T J thanks for pointing out the mistake i have updated the code although it did'nt fixed the problem but thank you for your effort ,and i already got the answer thanks anyway : ) – Shishir Tiwari May 10 '21 at 17:17
0

Dennis is correct about the strict mode behavior which is causing the issue here.

If you still need to use strict mode in the project you can use the code below to update the same-

Code Sandbox Link

Vishal
  • 404
  • 3
  • 9
  • @Visal thanks for your answer as you said dennis is correct also i wanted to see what was wrong with this code i already had some solutions in my mind thanks again for your valuable time – Shishir Tiwari May 10 '21 at 17:55