I am new to hooks so I will ask this question:
How can I legally use setState inside a onChange function ??
{
const state = useState([{date: date}]);
};
This throws an error :(
Expected: update the state
And result error message: Hooks can only be called inside the body of a function component.
I did find some answers on the web about multiple React version installed but this is not the case here :)
You need to declare your hook in the body of your main component function:
const MyComponent = () => {
// declaring your useState Hook - it returns a getter and setter
const [date, setDate] = useState(null)
const myCallback = (newDate)=>{
// you can read the state
const state = date;
// or, you can do the equivalent of setState
setDate(newDate)
}
return // return your .jsx
}
The things to note are that you declare your hook once, and that it returns two parameters, a getter and a setter. From then on, you interact with that original Hook using the getter and setter only.
For your pastebin code:
const [myState,setMyState] = useState(
{
name: "",
type: props.navigation.getParam("serviceName"),
date: "15-05-2018",
imageURI: props.navigation.getParam("imageURI")
});
const handleChange = e => {
setMyState(prevState=> {...prevState, name: e.nativeEvent.text})
};
(I've used myState and setMyState but they can be whatever you like)
Hmm it seems you've got it wrong...
There's no single state when we use hooks like in the case of a class,
const [item, setValue] = useState(null)
this sets the state variable "item" with initial value of null. Now we can use the "setValue" to change the value of "item" to what we want like this
setValue(5).There is no setState as in the case of a class component. Since the value of a state variable has been changed the component rerenders. We can declare multiple state-variables like
const [newitem, setnewItemValue] = useState(null) and whenever a state-variable changes the component will rerender
Related
I am new to react Hooks. Am trying to make use of useState in my code. While I was using it I found a term "Lazy initial state"
https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
But I am not able to think of any use case where this lazy initialising of state will be useful.
Like say my DOM is rendering and it needs the state value, but my useState has not initialised it yet! And say if you have rendered the DOM and the someExpensiveComputation has finished, the DOM will re-render!
The value passed to the useState hook in the initial render is the initial state value, and gets disregarded in subsequent renders. This initial value can be the result of calling a function as in the following situation:
const Component = () => {
const [state, setState] = useState(getInitialHundredItems())
}
But note that getInitialHundredItems is unconditionally and needlessly called on each render cycle.
For use cases like this instead of just calling a function that returns a value you can pass a function which returns the initial state. This function will only be executed once (initial render) and not on each render like the above code will. See Lazy Initial State for details.
const Component = () =>{
const [state, setState] = useState(getInitialHundredItems)
}
I'm refactoring to use Hooks and I've hit a very confusing wall
I have a basic functional component like so:
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
console.log(value, val); // abc undefined
return (...)
}
The log returns abc undefined - i.e. value in props is definitely defined, but the first argument returned from useState(value) is undefined
Just to test that hooks were working at all, I tried useState("abc") and that logs abc as expected.
I have no idea what I'm doing wrong - any ideas?
React version: 16.8.6
EDIT here is the parent component - nothing fancy going on here as far as I can see!
<MakeComponent
path={key}
value={item[key]}
info={value}
update={updateDiffs}
/>
As it is alluded to in the comments, useState(initialState) (or whatever you call it) will only use initialState on the first render.
During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState).
(React Docs, emphasis mine)
After this, on re-renders the useState function does not change the state based on new changes to the props passed in.
To make changes reflected everytime value changes, register an effect on the input prop like so
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => { setVal(value)}, [value] )
return (...)
}
Note that just setting state based on the props changing is a bit of an anti-pattern, as MonsterBucket notices you could just rely directly on the props changing to trigger a re-render:
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
if (val !== value) { // don't update unnecessarily
setVal(value);
}
return (...)
}
And instead reserve useEffect for side effects, mostly those outside of the React render cycle.
To see examples of these, have a look as these ReactJs docs - You might not need an effect, which covers lots of other examples.
Here you have to add useEffect if you want to update the state on props change, which will listen to prop change & will update the state value accordingly
here is the updated snippet
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => { setVal(value)}, [value] )
return (<div>{val}</div>)
}
Attching sandbox link
https://codesandbox.io/s/confident-agnesi-ohkq7?file=/src/MakeComponent.js
By the time you pass the prop value to useState the value of it can be yet to set. value itself might have been undefined yet.
Also setState is not truly sync so if the useState uses same mechanism as setState your state val might not be set to value yet as the initial value.
In such cases, using props as the initial values to states, you should use a side effect with no dependency. Once the first render achieved the effect will run and set your state with prop. You can let the initial value of the component be undefined passing nothing with no problems.
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState();
// get initial state after initial render
useEffect(() => {
setVal(value)
}, [])
console.log(value, val); // abc undefined then will log abc abc
return (...)
}
Just keep in mind that props in React are meant to be read-only, state is for read and write. But it is perfectly fine, and no not an anti pattern, if you use a prop just as an initial value for a state and use the state you have set with the prop after that instead of the prop. That is for consistency since you might have two different values at a time from a prop and a state in circumstances.
Your case might need to care for the value of the prop at an arbitrary time depending on you needs as stressed in one of the above answers. You question does not state that. Still, if so, you can add the prop to the dependency array of the effect that sets the state with that prop, and yes write separate effects for other props you want the same, well, effect.
If you don't need writing for that data you do not need that pattern at all, just use prop and it will be up to date and you will have consistency. But you apparently do so I hope the pattern I suggest above works for you as it does for me.
I am new to react Hooks. Am trying to make use of useState in my code. While I was using it I found a term "Lazy initial state"
https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
But I am not able to think of any use case where this lazy initialising of state will be useful.
Like say my DOM is rendering and it needs the state value, but my useState has not initialised it yet! And say if you have rendered the DOM and the someExpensiveComputation has finished, the DOM will re-render!
The value passed to the useState hook in the initial render is the initial state value, and gets disregarded in subsequent renders. This initial value can be the result of calling a function as in the following situation:
const Component = () => {
const [state, setState] = useState(getInitialHundredItems())
}
But note that getInitialHundredItems is unconditionally and needlessly called on each render cycle.
For use cases like this instead of just calling a function that returns a value you can pass a function which returns the initial state. This function will only be executed once (initial render) and not on each render like the above code will. See Lazy Initial State for details.
const Component = () =>{
const [state, setState] = useState(getInitialHundredItems)
}
I created component which take props and this component filter the props but whenever state is changed it re-assign the variable.
Example:
const Foo = (props) => {
const [state, setState] = useState();
const schema = array_filter(props.data, "schema");
// other Code
}
Everytime when state is changed array_filter method is called. array_filter function is custom function which filter the array. I want to avoid extra running of array_filter function. How can I do that can you please let me know.
I try useRef but not working.
Just cache your result and it only re-calculates as your input is changed which is best in your case:
const schema = React.useMemo(() => array_filter(props.data, "schema"), [props.data]);
I am looking for a way to perform more advanced comparison instead of the second parameter of the useEffect React hook.
Specifically, I am looking for something more like this:
useEffect(
() => doSomething(),
[myInstance],
(prev, curr) => { /* compare prev[0].value with curr[0].value */ }
);
Is there anything I missed from the React docs about this or is there any way of implementing such a hook on top of what already exists, please?
If there is a way to implement this, this is how it would work: the second parameter is an array of dependencies, just like the useEffect hook coming from React, and the third is a callback with two parameters: the array of dependencies at the previous render, and the array of dependencies at the current render.
you could use React.memo function:
const areEqual = (prevProps, nextProps) => {
return (prevProps.title === nextProps.title)
};
export default React.memo(Component, areEqual);
or use custom hooks for that:
How to compare oldValues and newValues on React Hooks useEffect?
In class based components was easy to perform a deep comparison. componentDidUpdate provides a snapshot of previous props and previous state
componentDidUpdate(prevProps, prevState, snapshot){
if(prevProps.foo !== props.foo){ /* ... */ }
}
The problem is useEffect it's not exactly like componentDidUpdate. Consider the following
useEffect(() =>{
/* action() */
},[props])
The only thing you can assert about the current props when action() gets called is that it changed (shallow comparison asserts to false). You cannot have a snapshot of prevProps cause hooks are like regular functions, there aren't part of a lifecycle (and don't have an instance) which ensures synchronicity (and inject arguments). Actually the only thing ensuring hooks value integrity is the order of execution.
Alternatives to usePrevious
Before updating check if the values are equal
const Component = props =>{
const [foo, setFoo] = useState('bar')
const updateFoo = val => foo === val ? null : setFoo(val)
}
This can be useful in some situations when you need to ensure an effect to run only once(not useful in your use case).
useMemo:
If you want to perform comparison to prevent unecessary render calls (shoudComponentUpdate), then useMemo is the way to go
export React.useMemo(Component, (prev, next) => true)
But when you need to get access to the previous value inside an already running effect there is no alternatives left. Cause if you already are inside useEffect it means that the dependency it's already updated (current render).
Why usePrevious works
useRef isn't just for refs, it's a very straightforward way to mutate values without triggering a re render. So the cycle is the following
Component gets mounted
usePrevious stores the inital value inside current
props changes triggering a re render inside Component
useEffect is called
usePrevious is called
Notice that the usePrevious is always called after the useEffect (remember, order matters!). So everytime you are inside an useEffect the current value of useRef will always be one render behind.
const usePrevious = value =>{
const ref = useRef()
useEffect(() => ref.current = value,[value])
}
const Component = props =>{
const { A } = props
useEffect(() =>{
console.log('runs first')
},[A])
//updates after the effect to store the current value (which will be the previous on next render
const previous = usePrevious(props)
}
I hit the same problem recently and a solution that worked for me is to create a custom useStateWithCustomComparator hook.
In your the case of your example that would mean to replace
const [myInstance, setMyInstance] = useState(..)
with
const [myInstance, setMyInstance] = useStateWithCustomComparator(..)
The code for my custom hook in Typescript looks like that:
const useStateWithCustomComparator = <T>(initialState: T, customEqualsComparator: (obj1: T, obj2: T) => boolean) => {
const [state, setState] = useState(initialState);
const changeStateIfNotEqual = (newState: any) => {
if (!customEqualsComparator(state, newState)) {
setState(newState);
}
};
return [state, changeStateIfNotEqual] as const;
};