Navish
Navish

Reputation: 151

React state hook with an object

const App = () => {
  const [User, setUser] = useState({
    id: 1,
    name: "ed",
    age: Number,
    edit: false
  });
  return (
    <div>
      <input value={User.name} onChange={e => setUser.name(e.target.value)} />
    </div>
  );
};

I am working with react hooks. I set the initial state to an object. I try changing the value with react hooks but this gives an error TypeError: setUser.name is not a function

Upvotes: 5

Views: 7270

Answers (3)

Ben Carp
Ben Carp

Reputation: 26518

Lets look at the value of User and setUser

const [User, setUser] = useState({
    id: 1,
    name: "ed",
    age: Number,
    edit: false
  });

React.useState returns a value and a setter to that value. setUser is a function. So while User.name is a string with the initial value of "ed" setUser.name doesn't exist. The error TypeError: setUser.name is not a function is the result of trying to pass an undefined value an argument. One way of solving this is as Murtaza Hussain and other answers suggest:

  <input 
    value={User.name}
    onChange={e => setUser(prev => ({...prev, name: e.target.value}))}
  />

As an alternative you can use a custom useObjState hook, which provides a rather simple API.

UseObjState hook

const useObjState = initialObj => {
  const [obj, setObj] = React.useState(initialObj);
  const setObjHelper = useMemo( () => { // the value of setObjHelper is permanent, so even if it is passed to a child component, it shouldn't require extra component updates
    const helper = {}
    Object.keys(initialObj).forEach(key => {
      helper[key] = newVal => setObj({ ...obj, [key]: newVal });
    });
    return helper
  }, [])
  return [obj, setObjHelper];
};

It provides a much easier/cleaner way to update the value of the name property.

function App() {
  const [user, setUser] = useObjState({
    id: 1,
    name: "ed",
    age: Number,
    edit: false
  });

  return (
    <input 
      value={user.name} 
      onChange={e => setUser.name(e.target.value)} />
    />
  )
}

Demo

Upvotes: 4

Shubham Khatri
Shubham Khatri

Reputation: 281606

setUser is a function which you use to update the state and since it just replaces the state you need to merge your previous state value too. Also event is cleared in callback so you need to get the value before using the callback of setUser. Its better to write a handler function for this. Also you can write a generic function to handle setting all values

const App = () => {
      const [User, setUser] = React.useState({
        id: 1,
        name: "ed",
        age: Number,
        edit: false
      }); 
      const handleChange = (e) => {
         const {value, name} = e.target;
         setUser(prev => ({...prev, [name]: val}))
      }
      return (
        <div>
          <input value={User.name} onChange={handleChange} />
        </div>
      );
    };
    ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />

Upvotes: 8

Murtaza Hussain
Murtaza Hussain

Reputation: 4285

setUser is a function same as setState.. for more details check hooks

  <input 
    value={User.name}
    onChange={e => setUser(prev => ({...prev, name: e.target.value}))}
  />

in your context:

const App = () => {
  const [User, setUser] = useState({
    id: 1,
    name: "ed",
    age: Number,
    edit: false
  });
  return (
    <div>
      <input 
        value={User.name}
        onChange={e => setUser(prev => ({...prev, name: e.target.value}))}
      />
  );
};

Upvotes: 1

Related Questions