useState always is default value in itself - javascript

I'm using useState to manage the state, it's working fine. But when i return state inside itseft, it always value of initial
import react, {useState} from 'react'
const MyComponent = () => {
const [myState, setMyState] = useState({
value: 'initial value',
setValue: (newValue) => {
setMyState({...myState, value: newValue})
console.log(myState.value) //<--always is 'initial value'
}
})
return(
<>
<p>{myState.value}</p> //<-- it's working fine
<input value={myState.value} onChange={(e) => myState.setValue(e.target.value)} /> //<--working fine too
</>
)
}
I expect the console.log is value of input, but the actual output always is initial value

const [myState, setMyState] = useState({
value: 'initial value',
setValue: (newValue) => {
setMyState({...myState, value: newValue})
console.log(myState.value) //<--always is 'initial value'
}
})
The first time your component function is run, the setValue function captures the initial value of myState. The second time it is run, you make a copy of the setValue function—but this is the function that has has captured the initial value of myState. It never updates.
Since the function never changes, you should not put it in useState() in the first place. You can define the function separately.
const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = (newValue) => {
setMyState({ ...myState, value: newValue })
}
Now, a new setValue copy is created every time the component function is run. When capturing variables, you can use useCallback() for optimization; if the values didn't change, React will reuse old copies of the function.
const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = useCallback((newValue) => {
setMyState({ ...myState, value: newValue })
}, [myState]) // ← this bit ensures that the value is always up to date inside the callback
As mentioned by Shubham Khatri, there is an even faster and better approach in this situation: using the functional form of setState.
const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = useCallback((newValue) => {
setMyState((prev) => ({ ...prev, value: newValue }))
}, []) // ← now we use an empty array, so React will never update this callback
Any of these three methods are fine to use, though; they will all work and perform good enough for most use cases.
Per comments, you're attempting to create an object that is passed down via context. A way to do this is by creating the context object in a separate step, similarly to how we created the callback function. This time, we use useMemo, which is similar to useCallback but works for any type of object.
// Per T.J. Crowder's answer, you can use separate `useState` calls if you need multiple values.
const [myState, setMyState] = useState('initial value')
const ctx = useMemo(() => ({
value: myState,
setValue: (newValue) => {
setMyState(newValue)
}
}), [myState])
return (
<Provider value={ctx}>
{children}
</Provider>
)

goto-bus-stop's answer explains why you're getting the problem you're getting. But there's another thing to address here:
From the code you've provided, it looks like you're using an object as your state value. In particular, this line:
setMyState({...myState, value: newValue})
..suggests you intend myState to have multiple things in it, with value just being one of them.
That's not how you do it with hooks. It's fine to have an object as a state value, but generally you do that when you're going to update (that is, replace) the entire object when the state changes. If you're updating individual parts of the state (as suggested by the above), you use individual useState calls instead of an object. See comments:
const {useState} = React;
const MyComponent = () => {
// Separate `useState` calls for each state item
const [value, setValue] = useState('initial value');
const [anotherValue, setAnotherValue] = useState('another value');
// No need to create your own update function, that's what `setValue` and
// `setAnotherValue` are, just use them:
return(
<React.Fragment>
<p>{value}</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<p>{anotherValue}</p>
<input value={anotherValue} onChange={(e) => setAnotherValue(e.target.value)} />
</React.Fragment>
);
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
This separation is particularly useful if you have any side effects of state changes, because you can specify what state triggers the side effect. For instance, here's the component above which triggers a console.log when value changes but not when anotherValue changes, and another effect that occurs when either of them changes:
const {useState, useEffect} = React;
const MyComponent = () => {
// Separate `useState` calls for each state item
const [value, setValue] = useState('initial value');
const [anotherValue, setAnotherValue] = useState('another value');
// A counter for all changes; we use -1 because
// our effect runs on initial mount
const [changes, setChanges] = useState(-1);
// No need to create your own update function, that's what `setValue` and
// `setAnotherValue` are, just use them:
// A side-effect for when `value` changes:
useEffect(() => {
console.log(`value changed to ${value}`);
}, [value]); // <=== Notice that we declare what triggers this effect
// A side-effect for when *either* `value` or `anotherValue` changes:
useEffect(() => {
setChanges(changes + 1);
}, [value, anotherValue]);
return(
<React.Fragment>
<p>Total changes: {changes}</p>
<p>{value}</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<p>{anotherValue}</p>
<input value={anotherValue} onChange={(e) => setAnotherValue(e.target.value)} />
</React.Fragment>
);
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

A better way to do this will be
import react, {useState} from 'react'
const MyComponent = () => {
const [ value, setValue ] = useState('initial value');
const handleChange = (e) => {
setValue(e.target.value);
}
return(
<>
<p>{myState}</p>
<input value={myState.value} onChange={handleChange} />
</>
)
}

Firstly the function inside the useState argument isn't aware of the update since its only called once and has the value from its closure. Secondly the way you are using useState is not correct, you must instead only have the value in useState and have the handlers outside
Also you must use callback pattern
import react, {useState} from 'react'
const MyComponent = () => {
const [myState, setMyState] = useState('initial value');
const setValue = (newValue) => {
setMyState(newValue)
}
console.log(myState);
return(
<>
<p>{myState}</p>
<input value={myState} onChange={(e) => setValue(e.target.value)} />
</>
)
}
Also state update is asynchronous so the update is not reflected immediately instead after the next render

Related

React Hooks: How to handle multiple onChange and onClick events in map [duplicate]

I have a question, if I can use useState generic in React Hooks, just like I can do this in React Components while managing multiple states?
state = {
input1: "",
input2: "",
input3: ""
// .. more states
};
handleChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value,
});
};
Yes, with hooks you can manage complex state (without 3rd party library) in three ways, where the main reasoning is managing state ids and their corresponding elements.
Manage a single object with multiple states (notice that an array is an object).
Use useReducer if (1) is too complex.
Use multiple useState for every key-value pair (consider the readability and maintenance of it).
Check out this:
// Ids-values pairs.
const complexStateInitial = {
input1: "",
input2: "",
input3: ""
// .. more states
};
function reducer(state, action) {
return { ...state, [action.type]: action.value };
}
export default function App() {
const [fromUseState, setState] = useState(complexStateInitial);
// handle generic state from useState
const onChangeUseState = (e) => {
const { name, value } = e.target;
setState((prevState) => ({ ...prevState, [name]: value }));
};
const [fromReducer, dispatch] = useReducer(reducer, complexStateInitial);
// handle generic state from useReducer
const onChangeUseReducer = (e) => {
const { name, value } = e.target;
dispatch({ type: name, value });
};
return (
<>
<h3>useState</h3>
<div>
{Object.entries(fromUseState).map(([key, value]) => (
<input
key={key}
name={key}
value={value}
onChange={onChangeUseState}
/>
))}
<pre>{JSON.stringify(fromUseState, null, 2)}</pre>
</div>
<h3>useReducer</h3>
<div>
{Object.entries(fromReducer).map(([key, value]) => (
<input
name={key}
key={key}
value={value}
onChange={onChangeUseReducer}
/>
))}
<pre>{JSON.stringify(fromReducer, null, 2)}</pre>
</div>
</>
);
}
Notes
Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
Refer to React Docs.
The correct way to do what you're trying to do is to create your own hook that uses useState internally.
Here is an example:
// This is your generic reusable hook.
const useHandleChange = (initial) => {
const [value, setValue] = React.useState(initial);
const handleChange = React.useCallback(
(event) => setValue(event.target.value), // This is the meaty part.
[]
);
return [value, handleChange];
}
const App = () => {
// Here we use the hook 3 times to show it's reusable.
const [value1, handle1] = useHandleChange('one');
const [value2, handle2] = useHandleChange('two');
const [value3, handle3] = useHandleChange('three');
return <div>
<div>
<input onChange={handle1} value={value1} />
<input onChange={handle2} value={value2} />
<input onChange={handle3} value={value3} />
</div>
<h2>States:</h2>
<ul>
<li>{value1}</li>
<li>{value2}</li>
<li>{value3}</li>
</ul>
</div>
}
ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Note the use of React.useCallback to stop your hook from returning a new handler function on every render. (We don't need to specify setValue as a dependency because React guarantees that it will never change)
I didn't actually test this, but it should work.
See https://reactjs.org/docs/hooks-reference.html#usestate for more info.
import React, {useState} from 'react';
const MyComponent = () => {
const [name, setName] = useState('Default value for name');
return (<div><button onClick={()=>setName('John Doe')}}>Set Name</button></div>);
};
export default MyComponent;

Is there an easier way to handle many useState hooks? [duplicate]

I have a question, if I can use useState generic in React Hooks, just like I can do this in React Components while managing multiple states?
state = {
input1: "",
input2: "",
input3: ""
// .. more states
};
handleChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value,
});
};
Yes, with hooks you can manage complex state (without 3rd party library) in three ways, where the main reasoning is managing state ids and their corresponding elements.
Manage a single object with multiple states (notice that an array is an object).
Use useReducer if (1) is too complex.
Use multiple useState for every key-value pair (consider the readability and maintenance of it).
Check out this:
// Ids-values pairs.
const complexStateInitial = {
input1: "",
input2: "",
input3: ""
// .. more states
};
function reducer(state, action) {
return { ...state, [action.type]: action.value };
}
export default function App() {
const [fromUseState, setState] = useState(complexStateInitial);
// handle generic state from useState
const onChangeUseState = (e) => {
const { name, value } = e.target;
setState((prevState) => ({ ...prevState, [name]: value }));
};
const [fromReducer, dispatch] = useReducer(reducer, complexStateInitial);
// handle generic state from useReducer
const onChangeUseReducer = (e) => {
const { name, value } = e.target;
dispatch({ type: name, value });
};
return (
<>
<h3>useState</h3>
<div>
{Object.entries(fromUseState).map(([key, value]) => (
<input
key={key}
name={key}
value={value}
onChange={onChangeUseState}
/>
))}
<pre>{JSON.stringify(fromUseState, null, 2)}</pre>
</div>
<h3>useReducer</h3>
<div>
{Object.entries(fromReducer).map(([key, value]) => (
<input
name={key}
key={key}
value={value}
onChange={onChangeUseReducer}
/>
))}
<pre>{JSON.stringify(fromReducer, null, 2)}</pre>
</div>
</>
);
}
Notes
Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
Refer to React Docs.
The correct way to do what you're trying to do is to create your own hook that uses useState internally.
Here is an example:
// This is your generic reusable hook.
const useHandleChange = (initial) => {
const [value, setValue] = React.useState(initial);
const handleChange = React.useCallback(
(event) => setValue(event.target.value), // This is the meaty part.
[]
);
return [value, handleChange];
}
const App = () => {
// Here we use the hook 3 times to show it's reusable.
const [value1, handle1] = useHandleChange('one');
const [value2, handle2] = useHandleChange('two');
const [value3, handle3] = useHandleChange('three');
return <div>
<div>
<input onChange={handle1} value={value1} />
<input onChange={handle2} value={value2} />
<input onChange={handle3} value={value3} />
</div>
<h2>States:</h2>
<ul>
<li>{value1}</li>
<li>{value2}</li>
<li>{value3}</li>
</ul>
</div>
}
ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Note the use of React.useCallback to stop your hook from returning a new handler function on every render. (We don't need to specify setValue as a dependency because React guarantees that it will never change)
I didn't actually test this, but it should work.
See https://reactjs.org/docs/hooks-reference.html#usestate for more info.
import React, {useState} from 'react';
const MyComponent = () => {
const [name, setName] = useState('Default value for name');
return (<div><button onClick={()=>setName('John Doe')}}>Set Name</button></div>);
};
export default MyComponent;

React Context Api and State hooks mutation

I have a modern react application using the context api and many hooks, i use the context to store global values for my application, this values or the context itself should never directly re-render other components, the context itself has a its own getter/setter in form of the UseState hook/s which is what is called from the consumer components to be used, if any component is dependent on the context data a separate state in this component itself is created and state is then being properly handled.
My concrete question in my case how bad is it to directly mutate the object i have stored in the context?
for example from any random consumer component changing the context object as follows:
const handlerFunction = () => {contextObjData.value = "Something"};
Instead of the "intended" react way:
const handlerFunction = () => {setContextObjData(...contextObjData, value: "Something")};
To me it seems overkill to each time save the entire object again but maybe someone can give me another perspective and some insights.
Side question kind of nooby but i am not sure, is there a difference between these two:
const handlerFunction = () => {setContextObjData(...contextObjData, value: "Something")};
const handlerFunction = () => {setContextObjData(prevState => ({...contextObjData, value: "Something"}));
A state change will trigger a render. When you mutate something then React won't detect that the state has changed and will not re render.
The handlerFunction examples matter only if you want to optimize it using useCallback but the way you do it it is broken either way (syntax error and not using prevState in the second example).
//handlerFunction will be re created every render
const handlerFunction = () =>
setContextObjData({
...contextObjData,
value: 'Something',
});
//handler function will only be created on mount
const optimizedHandler = React.useCallback(
() =>
setContextObjData((prevState) => ({
...prevState,
value: 'Something',
})),
[] //empty dependency, only create optimizedHandler on mount
);
//broken handler, using stale closure will give you liner warning
const brokenHandler = React.useCallback(
() =>
//callback only used on mount so has contextObjData
// as it was when mounted (stale closure)
setContextObjData({
...contextObjData, //needs contextObjData in closure scope
value: 'Something',
}),
[] //empty dependency, but contextObjData will be a stale closure
);
Pure components will only re render when props change, state change or return value from useSelector or useContext change.
When you pass a callback as a prop to a child and your component re renders without the child needing to re render you can optimize a passed callback with useCallback so the child doesn't get needlessly re rendered:
//Child is a pure component
const Child = React.memo(function Increment({ increment }) {
const r = React.useRef(0);
r.current++;
return (
<button onClick={increment}>
rendered: {r.current} times, click to increment
</button>
);
});
const Parent = () => {
const [count, setCount] = React.useState(1);
const increment = React.useCallback(
() => setCount((c) => c + 1),
[]
);
React.useEffect(() => {
const t = setInterval(() => increment(), 1000);
return () => clearInterval(t);
}, [increment]);
return (
<div>
<h4>{count}</h4>
<Child increment={increment} />
</div>
);
};
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Here is an example how mutation will break re rendering:
const CounterContext = React.createContext();
const CounterProvider = ({ children }) => {
console.log('render counter provider');
const [c, setC] = React.useState({ count: 0 });
const increment = React.useCallback(
() =>
setC((c) => {
console.log('broken:', c.count);
c.count++;
return c;
}), //broken, context users never re render
[]
);
return (
<CounterContext.Provider value={[c, increment]}>
{children}
</CounterContext.Provider>
);
};
const App = () => {
console.log('render App');
const [count, increment] = React.useContext(
CounterContext
);
return (
<div>
<h4>count: {count.count}</h4>
<button onClick={increment}>+</button>
</div>
);
};
ReactDOM.render(
<CounterProvider>
<App />
</CounterProvider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Usage of useCallback and setting new object state using previous state as argument

Consider this basic form fields component with a custom form hook to handle input changes:
import React, { useState, useCallback } from 'react';
const useFormInputs = (initialState = {})=> {
const [values, setValues] = useState(initialState);
const handleChange = useCallback(({ target: { name, value } }) => {
setValues(prev => ({ ...prev, [name]: value }));
}, []);
const resetFields = useCallback(() =>
setValues(initialState), [initialState]);
return [values, handleChange, resetFields];
};
const formFields = [
{ name: 'text', placeholder: 'Enter text...', type: 'text', text: 'Text' },
{ name: 'amount', placeholder: 'Enter Amount...', type: 'number',
text: 'Amount (negative - expense, positive - income)' }
];
export const AddTransaction = () => {
const [values, handleChange, resetFields] = useFormInputs({
text: '', amount: ''
});
return <>
<h3>Add new transaction</h3>
<form>
{formFields.map(({ text, name, ...attributes }) => {
const inputProps = { ...attributes, name };
return <div key={name} className="form-control">
<label htmlFor={name}>{text}</label>
<input {...inputProps} value={values[name]}
onChange={handleChange} />
</div>;
})}
<button className="btn">Add transaction</button>
</form>
<button className="btn" onClick={resetFields}>Reset fields</button>
</>;
};
Is there really any reason / advantage for me to use useCallback to cache the function in my custom hook? I read the docs, but I just coudln't grasp the idea behind this usage of useCallback. How exactly it memoizes the function between renders? How exactly does ti work, and should I use it?
Inside the same custom hook, you can see the new values state being updated by spreading the previous state and creating a new object like so: setValues(prev => ({ ...prev, [name]: value }));
Would there be any difference if I did this instead? setValues({ ...prev, [name]: value })
as far as I can tell, doesn't look like it has any difference right? I am simply accessing the state directly.. Am I wrong?
Your first question:
In your case it doesn't matter because everything is rendered in the same component. If you have a list of things that get an event handler then useCallback can save you some renders.
In the example below the first 2 items are rendered with an onClick that is re created every time App re renders. This will not only cause the Items to re render it will also cause virtual DOM compare to fail and React will re create the Itms in the DOM (expensive operation).
The last 2 items get an onClick that is created when App mounts and not re created when App re renders so they will never re render.
const { useState, useCallback, useRef, memo } = React;
const Item = memo(function Item({ onClick, id }) {
const rendered = useRef(0);
rendered.current++;
return (
<button _id={id} onClick={onClick}>
{id} : rendered {rendered.current} times
</button>
);
});
const App = () => {
const [message, setMessage] = useState('');
const onClick = (e) =>
setMessage(
'last clicked' + e.target.getAttribute('_id')
);
const memOnClick = useCallback(onClick, []);
return (
<div>
<h3>{message}</h3>
{[1, 2].map((id) => (
<Item key={id} id={id} onClick={onClick} />
))}
{[1, 2].map((id) => (
<Item key={id} id={id} onClick={memOnClick} />
))}
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Another example is when you want to call a function in an effect that also needs to be called outside of the effect so you can't put the function inside the effect. You only want to run the effect when a certain value changes so you can do something like this.
//fetchById is (re) created when ID changes
const fetchById = useCallback(
() => console.log('id is', ID),
[ID]
);
//effect is run when fetchById changes so basically
// when ID changes
useEffect(() => fetchById(), [fetchById]);
Your second question:
The setValues({ ...prev, [name]: value }) will give you an error because you never defined pref but if you meant: setValues({ ...values, [name]: value }) and wrap the handler in a useCallback then now your callback has a dependency on values and will be needlessly be re created whenever values change.
If you don't provide the dependency then the linter will warn you and you end up with a stale closure. Here is an example of the stale closure as counter.count will never go up because you never re create onClick after the first render thus the counter closure will always be {count:1}.
const { useState, useCallback, useRef } = React;
const App = () => {
const [counts, setCounts] = useState({ count: 1 });
const rendered = useRef(0);
rendered.current++;
const onClick = useCallback(
//this function is never re created so counts.count is always 1
// every time it'll do setCount(1+1) so after the first
// click this "stops working"
() => setCounts({ count: counts.count + 1 }),
[] //linter warns of missing dependency count
);
return (
<button onClick={onClick}>
count: {counts.count} rendered:{rendered.current}
</button>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Is there a generic way to set state in React Hooks? How to manage multiple states?

I have a question, if I can use useState generic in React Hooks, just like I can do this in React Components while managing multiple states?
state = {
input1: "",
input2: "",
input3: ""
// .. more states
};
handleChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value,
});
};
Yes, with hooks you can manage complex state (without 3rd party library) in three ways, where the main reasoning is managing state ids and their corresponding elements.
Manage a single object with multiple states (notice that an array is an object).
Use useReducer if (1) is too complex.
Use multiple useState for every key-value pair (consider the readability and maintenance of it).
Check out this:
// Ids-values pairs.
const complexStateInitial = {
input1: "",
input2: "",
input3: ""
// .. more states
};
function reducer(state, action) {
return { ...state, [action.type]: action.value };
}
export default function App() {
const [fromUseState, setState] = useState(complexStateInitial);
// handle generic state from useState
const onChangeUseState = (e) => {
const { name, value } = e.target;
setState((prevState) => ({ ...prevState, [name]: value }));
};
const [fromReducer, dispatch] = useReducer(reducer, complexStateInitial);
// handle generic state from useReducer
const onChangeUseReducer = (e) => {
const { name, value } = e.target;
dispatch({ type: name, value });
};
return (
<>
<h3>useState</h3>
<div>
{Object.entries(fromUseState).map(([key, value]) => (
<input
key={key}
name={key}
value={value}
onChange={onChangeUseState}
/>
))}
<pre>{JSON.stringify(fromUseState, null, 2)}</pre>
</div>
<h3>useReducer</h3>
<div>
{Object.entries(fromReducer).map(([key, value]) => (
<input
name={key}
key={key}
value={value}
onChange={onChangeUseReducer}
/>
))}
<pre>{JSON.stringify(fromReducer, null, 2)}</pre>
</div>
</>
);
}
Notes
Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
Refer to React Docs.
The correct way to do what you're trying to do is to create your own hook that uses useState internally.
Here is an example:
// This is your generic reusable hook.
const useHandleChange = (initial) => {
const [value, setValue] = React.useState(initial);
const handleChange = React.useCallback(
(event) => setValue(event.target.value), // This is the meaty part.
[]
);
return [value, handleChange];
}
const App = () => {
// Here we use the hook 3 times to show it's reusable.
const [value1, handle1] = useHandleChange('one');
const [value2, handle2] = useHandleChange('two');
const [value3, handle3] = useHandleChange('three');
return <div>
<div>
<input onChange={handle1} value={value1} />
<input onChange={handle2} value={value2} />
<input onChange={handle3} value={value3} />
</div>
<h2>States:</h2>
<ul>
<li>{value1}</li>
<li>{value2}</li>
<li>{value3}</li>
</ul>
</div>
}
ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Note the use of React.useCallback to stop your hook from returning a new handler function on every render. (We don't need to specify setValue as a dependency because React guarantees that it will never change)
I didn't actually test this, but it should work.
See https://reactjs.org/docs/hooks-reference.html#usestate for more info.
import React, {useState} from 'react';
const MyComponent = () => {
const [name, setName] = useState('Default value for name');
return (<div><button onClick={()=>setName('John Doe')}}>Set Name</button></div>);
};
export default MyComponent;

Categories

Resources