Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute appropriate useState dynamically from string names in react hooks?

Imagine a scenario that there's a same operation through different values (For instance generating a custom html element for different input values); in the case of using a component class, I would mocked this as follow:

const onFuncClicked = property => newVal => {
 this.setState({ [property]: newVal })
}

but what if i use react hooks:

  const onFuncClicked = property => newVal => {
    eval(`set${property}(${newVal})`);
  }

not only using eval is not recommended for thousands of reasons, but this code does not work at all!! It generates the correct useState function but the component does not know it even and gives a ReferenceError that that function (generated useState) is not defined

like image 495
Elmira Khodaee Avatar asked Aug 10 '19 12:08

Elmira Khodaee


People also ask

Can useState be a string?

To type the useState hook as an array of strings in React, use the hook's generic, e.g. const [names, setNames] = useState<string[]>([]) . The state variable can be initialized to an empty array or an array of strings and will only accept string values. Copied!

Can React Hooks be called conditionally?

The error "React hook 'useState' is called conditionally" occurs when we use the useState hook conditionally or after a condition that may return a value. To solve the error, move all React hooks above any conditionals that may return a value.

What is the best practice to use functions in useState hook?

Always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That's what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.


2 Answers

One way to approach this is to use a map of methods to the property names:

const [property, setProperty] = useState(defaultValue);

const methodMap = { propetryName: setPropertyName, /* ... more fields */ };

Then you can call it from your set method like so:

const onClicked = (property, value) => {
    methodMap[property](value);
}

Another option, and probably more common one, would be to have state as an object with all your properties and change them by prop name:

const [state, setState] = useState({property: value});

const onClicked = (property, value) => {
    setState(state => {...state, [property]: value});
}
like image 67
Clarity Avatar answered Oct 18 '22 08:10

Clarity


well, if you are already using hooks and useState and did end up with a bunch of them that make your code look too complex I suggest using useReducer, here is a sample from official documentation:


function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}```
like image 26
Pooria Khodaveissi Avatar answered Oct 18 '22 09:10

Pooria Khodaveissi