Reputation: 974
In Simple react class component we used to change the props to state this way:
constructor(props) {
super(props)
this.state = {
pitch: props.booking.pitch,
email: props.booking.email,
firstName: props.booking.firstName,
arrivalDate: props.booking.arrivalDate
}
}
But I am not aware of how to do it in new feature as Hooks, but I am trying to do it this way.
const GenerateDescHook = ({ description: initialDesc }) => {
const [description, setDescription] = useState(null)
useEffect(() => {
setDescription(initialDesc)
}, {})
function handleChangeVariance(newVariance) {
setDescription({
...description,
template: {
...description.template,
variance_name: newVariance,
},
})
}
}
Basically, I just need to change description props, which coming from another parent component to turn to state. Pls, could you show me how to do it in new way as Hooks way?
Upvotes: 38
Views: 43042
Reputation: 11
It is difficult to achieve a state which depends on prop value and at the same avoid unnecessary re-renders.
Here is my attempt in creating such a hook:
/**
* Replacement for useState which depends on prop value and avoids unnecessary re-renders.
*/
export function useStateBasedOnProps(propValue) {
const [, update] = useState(false);
const [state, setState] = useMemo(() => {
const state = {};
return [
state,
(value) => {
if (value instanceof Function) {
state.value = value(state.value);
} else {
state.value = value;
}
update((tick) => !tick);
}
];
}, [update]);
if (state.prevPropValue !== propValue) {
state.prevPropValue = propValue;
state.value = propValue;
}
return [state.value, setState];
}
Here is a usage example:
function IncButton({initialValue}) {
const [value, setValue] = useStateBasedOnProps(initialValue);
const increment = useCallback(() => setValue((prev) => prev + 1), [setValue]);
return <button onClick={increment}>Click to increment: {value}</button>;
}
Full example: https://gist.github.com/mdevils/b861610780300784292194f65886517a
Upvotes: 1
Reputation: 7621
The problem in your code is with {}
.
UseEffect hook expects and array, not an object. Use [initialDesc]
instead.
This is how you reset component state when props change
const GenerateDescHook = ({ description: initialDesc }) => {
const [description, setDescription] = useState(null)
useEffect(() => {
setDescription(initialDesc)
}, [initialDesc]);
}
This is how you initialize component state to prop value on first render only
const GenerateDescHook = ({ description: initialDesc }) => {
const [description, setDescription] = useState(initialDesc);
}
Upvotes: 82
Reputation: 7492
The initial value of your state is the one passed into useState
:
const GenerateDescHook = ({ description: initialDesc }) => {
const [description, setDescription] = useState(initialDesc)
As the documentation states :
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Is the equivalent of :
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
Upvotes: 2
Reputation: 4067
You can pass your initial state as first argument to useState
like this:
const GenerateDescHook = ({ description: initialDesc }) => {
const [description, setDescription] = useState(initialDesc)
...
Upvotes: 19