I have a function with code:
const { name, value, } = e.currentTarget;
this.setState({ [name]: value })
How can I make this construction with useState hook?
It's unpossible with useState since its modyfing function is stored in variable.
const [state, setState] = useState(initialState);
const { name, value, } = e.currentTarget;
const values = {[name]: value}
setState(prevState => {
return {...prevState, ...values};
});
Related
The problem is that calling unCheckAllColums updates the data of the set state (configEdit, configNoEdit) and additionally updates the value that comes from the props, and it should only update the configEdit
export fuction HookConfigsUser(configUser){
const initValues = {
configEdit: configUser; // this value only edit
configNoEdit: configUser; // this value use to recover info
}
const [values, setValues] = useState(initValues)
const unCheckAllColums = () => {
let editValue = { ...configEdit,}
let setListEdit = editValue.config.map((conf) => {
if (conf.enableEditShow === false) {
conf.show = true
} else {
conf.show = false
}
return conf
})
editValue.config = setListEdit
editValue.isEdit = true
const toSetAllstate = {
...values,
configEdit: editValue,
}
setValues(toSetAlstate)
}
const recoverInfo(){
setValues({
...values,
configEdit: values.configNoEdit,
}) }
return [unCheckAllColums, recoverInfo]
}
When calling the function all the variables are updated, those that are in setstate and the props and only the value inside state called configEdit should be updated.
In react, with useState, I can pass it an updater function to get the current state within the same function. Currently I update a state multiple times within the same function.
Is that possible with a reducer dispatch with useReducer?
Thanks.
const someFunc = () => {
// some other computation
const result = someOtherFunc();
setState((state)=>{
// do something with current state
return {...state, result}
})
const result2 = someOtherFunc2();
setState((state)=>{
// do something with current state
return {...state, result2}
})
}
Yes it is.
You can do this by grouping all your states in like an initialState variable:
import { useReducer, useEffect } from 'react';
const SampleComponent = () => {
const reducer = (state, action) => ({...state, ...action})
const initialState = {
someName: '',
someCounter: 0,
isLoading: false
}
const [{
someName, someCounter, isLoading
}, dispatch ] = useReducer(reducer, initialState )
useEffect(() => {
dispatch({ isLoading: true }
const res = await fetch('https://someapi')
const json = await res.json()
if (json) {
dispatch({
someName: json.someNameValue,
someCounter: someCounter + json.someCounterValue,
isLoading: false
})
}
}, [])
return (
<>
<h1>{someName}</h1>
<span>{someCounter}</span>
</>
)
}
There maybe other ways to do this, but the way works best for me is to:
Declare a reducer: const reducer = (state, action) => ({...state, action})
Declare all states group in an object state: const initialState = { someState: '' }
Use useReducer with the state variables and dispatch encapsulated with it: const [{ someState }, dispatch ] = useReducer(reducer, initialState)
Use dispatch() method to set new values of states: dispatch({ someState: newValue }) where newValue is any value you want to assign to someState.
Yes it is possible. Whatever that can be done with useState can also be done by useReducer. It is the context that makes us decide on which to use.
Suppose I just want to update my loading state, then useState is enough
const [isLoading, setIsLoading] = useState(true);
But let's suppose I have a input form,
if I decide to useState, then it will be kind of like this
const [name, setName] = useState('')
const [age, setAge] = useState(0);
const [address, setAddress] = useState('');
instead of using all these useState, I could directly using useReducer for this specific application
const initialFormValues = {
name: '',
age: 0,
address: '',
};
const reducer = (state, action) =>{
switch(action.type) {
case "NAME": return { ...state, name: action.value};
case "AGE": return { ...state, age: action.value};
case "ADDRESS": return { ...state, address: action.value};
}
}
const [formValues, dispatch] = useReducer(reducer, initialFormValues);
Now whenever you want to update your input fields in the form, just use dispatch.
Both can do the task. Depending upon the use-case, we can choose the more appropriate one.
I'm setting the value of an input to a property on an object (from the state). I have an onChange event to update the state. However, when I try to type, it doesn't actually update in the input. If I don't use an object and just have a string, it works fine. How can I fix this?
Demo of the issue
My code:
import React from "react";
export function App() {
const [data, setData] = React.useState({
test: "hello"
});
const change = (e) => {
data.test = e.target.value;
setData(data);
};
return <input value={data.test} onChange={change}></input>;
}
Per the comment by Aman Sadhwani, I used spread syntax to update the state without modifying the original object.
const change = (e) => {
setData({
...data,
test: e.target.value
});
};
Thanks, #Aman Sadhwani. Use Spread operator to update the state.
import React from "react";
export function App() {
const [data, setData] = React.useState({
test: "hello"
});
const change = (e) => {
setData({
...data
test:e.target.value
});
};
return <input value={data.test} onChange={change}></input>;
}
I am trying to compare old and new state values using custom hook usePrevious made with useRef hook where state consists of array of objects.
While printing the old value and current value, it returns current values in both cases, but when it's just the array of numbers or if it's the first render, it works well.
Also, https://codesandbox.io/s/4c4ie is the code for the test.
Is there any mistake I have done or there is something else to do to get old state and current state?
Below is the code I am using.
import React from 'react'
function usePrevious(value) {
const ref = React.useRef();
React.useEffect(() => {
ref.current = value;
});
return ref.current;
}
function Playground() {
const [state, setState] = React.useState([{ value: 0 }]);
const prevState = usePrevious(state);
React.useEffect(() => {
console.log(prevState, state)
if (prevState !== state) {
try {
console.log(prevState[0].value)
console.log(state[0].value)
} catch (e) {
}
}
}, [JSON.stringify(state)])
// }, [state])
const _onClick = () => {
const tempState = [...state];
tempState[0].value = state[0].value + 1;
setState(tempState)
}
return (
<div>
<div>prevStateValue: {prevState ? prevState[0].value : 'undefined'}</div>
<div>stateValue: {state[0].value}</div>
<button onClick={_onClick}>click</button>
</div>
)
}
export default Playground
You were mutating state with: tempState[0].value = state[0].value + 1;
Here is a working snippet:
function usePrevious(value) {
const ref = React.useRef();
React.useEffect(() => {
ref.current = value;
},[value]);//only set when value changed
return ref.current;
}
function App() {
const [state, setState] = React.useState([{ value: 0 }]);
const prevState = usePrevious(state);
React.useEffect(() => {
if (prevState !== state) {
try {
console.log(
'pref',
prevState[0].value,
'current',
state[0].value
);
} catch (e) {
console.log('not set yet');
}
}
}, [prevState, state]);
// }, [state])
const _onClick = () => {
const tempState = [...state];
//you were mutating state here
tempState[0] = {
...tempState[0],
value: tempState[0].value + 1,
};
setState(tempState);
};
return (
<div>
<div>
prevStateValue:{' '}
{prevState ? prevState[0].value : 'undefined'}
</div>
<div>stateValue: {state[0].value}</div>
<button onClick={_onClick}>click</button>
</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>
You are passing the reference of state to ref instead of the value. Cloning the object before assigning to ref.current will help.
function usePrevious(value) {
const ref = React.useRef();
React.useEffect(() => {
ref.current = JSON.parse(JSON.stringify(value));
});
return ref.current;
}
I have a Select custom component, that only have a select, options and listen for a onChange, then i have a useReducer code, that initialize with some variables, after select one option my state still have the initialized value
When i select ANOTHER the value in performSearch is ALL
const reducer = (state, newState) => ({ ...state, ...newState });
const [state, setState] = useReducer(reducer, {
filterStatus : 'ALL'
});
const performSearch = () => {
console.log(state.filterStatus) //<= first time is ALL, second time same value ALL, third, is another value
}
useEffect(() => {
performSearch()
},[])
<Select
onChange={(e) => {
const {value} = e.target
setState({filterStatus:value})
performSearch()
}}
items={[{key:"ALL",value:"ALL"},{key:"ANOTHER",value:"ANOTHER"}]}
/>
any idea?
If I had to guess I'd say that its because you are trying to call performSearch to log the console before the state is set. If you console log the state before you return your component you will probably be able to see the correct value in the state. I'm not sure what your use case is but if you want to use the value you can just return it in your function and not worry about the reducer and state at all. Like so:
const performSearch = (value) => {
console.log(value)
}
useEffect(() => {
performSearch('ALL')
},[])
<Select
onChange={(e) => {
const {value} = e.target
performSearch(value)
}}
items={[{key:"ALL",value:"ALL"},{key:"ANOTHER",value:"ANOTHER"}]}
/>
if you need to use the reducer then you can probably create a promise or I would just return the value to preformSearch and then set the state through your reducer from there like so:
const reducer = (state, newState) => ({ ...state, ...newState });
const [state, setState] = useReducer(reducer, {
filterStatus : 'ALL'
});
const performSearch = (value) => {
setState({filterStatus: value});
//Do your stuff with the value
console.log(value)
}
useEffect(() => {
//you can probably just set the value in preformSearch here manually or you can set it to the states value but setting to the states value would result in another render because you would set the state in performSearch again
performSearch(state.filterStatus)
},[])
<Select
onChange={(e) => {
const {value} = e.target
performSearch(value)
}}
items={[{key:"ALL",value:"ALL"},{key:"ANOTHER",value:"ANOTHER"}]}
/>
But like I said I'm not really sure what your end goal is with this component but for this use case I'm not sure you need to use the useReducer function at all.
The problem is that you are calling performSearch() inside the onChange function, so when you set the new state you will only see the value from the previous state. Try to put the performSearch function outside of the onSelect function and you will get the correct output.