Cannot read property React function component issue - javascript

I'm having this issue with a function component that it's giving me:
TypeError: Cannot read property 'name' of undefined
These are the parts that implements the 'name' input:
const initialState = { user: { name: '', email: '' }, list: []}
...
export default (props) =>
const [state, setState] = useState(initialState);
const user = state.user;
...
function handleChange (event){
setState({...state.user,
[event.target.name]: event.target.value})
}
<TextField
className="form-control"
name="name"
placeholder="Digite o nome..."
type="text"
value={state.user.name}
onChange={(e) => handleChange(e)}
required={true}
/>
I know I could easly do this with a Class Component, but i would like to do with a functional one, is it possible?

Issue
I think the issue is one, or both, of the following, both in the handleChange callback function.
You don't shallow copy the nested state correctly. {...state.user spreads the user values up a level to the root of the state object.
The onChange event has been nullified by the time the state update is processed.
Solution
useState state updater doesn't merge state updates, you need to shallow copy all parts of state that are being updated, and in this case that is state and state.user. I suggest also using a functional state update since you are updating from the previous state.
The onChange event is possibly nullified so you should destructure/save the input's name and value properties before enqueueing the state update.
Code:
function handleChange(event) {
const { name, value } = event.target; // <-- save name and value
setState(prevState => ({
...prevState, // <-- shallow copy previous state
user: {
...prevState.user, // <-- shallow copy previous state.user
[name]: value, // update property
},
}));
}

Related

How does prevState in new useState in React work ?? How does it store the value of previous state?

I know how to use prevState in new State, I want to know how does it actually works ?? How does this functionality works ?? why on putting in 'prevValue' in 'setFullName' function changes its value ?
import React, { useState } from "react";
function App() {
const [fullName, setFullName] = useState({
firstName: "",
lastName: ""
});
function handleChange(event) {
let value = event.target.value;
let name = event.target.name;
setFullName((prevValue) => {
if (name === "fName") {
return {
firstName: value,
lastName: prevValue.lastName
};
} else if (name === "lName") {
return {
firstName: prevValue.firstName,
lastName: value
};
}
});
}
return (
<div className="container">
<h1>
{fullName.firstName} {fullName.lastName}
</h1>
<form>
<input onChange={handleChange} name="fName" placeholder="First Name" />
<input onChange={handleChange} name="lName" placeholder="Last Name" />
<button>Submit</button>
</form>
</div>
);
}
export default App;
When a component is created (with React.createElement, or with JSX like <App />), React will see how many times useState is called in the component body, and will create an internal mapping of state indicies to state values. For example, with the following component:
const Comp = () => {
const [v1, setV1] = useState('a');
const [v2, setV12] = useState('b');
return <div>Text</div>;
};
When called, React will, internally, now have something like the following:
[ // array of all stateful values for this rendered component
'a', // corresponds to the stateful value of the first useState
'b', // corresponds to the stateful value of the second useState
]
These internal state values are changed when the state setter is called (eg setV1), and then when the component gets re-rendered, the new stateful values (coming from React internals) are then returned by useState.
When the state setter is passed a callback, eg, with your
setFullName((prevValue) => {
All that's really needed for this to work is for React to pass the current corresponding state value in React's internals as prevValue.
When state is set, the corresponding value in React's internals gets updated, but there may be other synchronous code that runs before the component gets re-rendered, and the new values (coming from React internals) get returned by the next invocations of useState. That's why using the callback form of the state setter sometimes gives different results - the callback argument always comes from React's internals, but the state value returned by useState only refers to the state at the time the component was rendered.
For example
const [count, setCount] = useState(0);
const clickHandler = () => {
setCount(count + 1); // sets the state in React's internals to 1
setCount(count + 1); // sets the state in React's internals to 1, not 2;
// because `count` still refers to the initial value of 0
};
const [count, setCount] = useState(0);
const clickHandler = () => {
setCount(count + 1); // sets the state in React's internals to 1
setCount(prevCount => prevCount + 1); // sets the state in React's internals to 2
// because `prevCount` comes directly from React's internals
};
But, for your code here, because multiple state changes are not occurring in the same render, there's no need to use the callback form - feel free to use the outside fullName identifier instead of using the prevValue and a callback.

React clearing all keys rather than shallow merging single key

I am pulling in data from an API, and then allowing a user to modify that data within react, after which I will send the updated state to the server.
My API provides the following JSON:
{
"id": 1,
"title": "Test Campaign",
"impressions": 12,
}
And my component looks like so:
function Campaign() {
const [data, setData] = useState([])
useEffect(async () => {
const result = await axios('/campaigns/1')
setData(result.data);
}, []);
const handleChange = (event) => {
setData({[event.target.name]: event.target.value});
}
return (
<div className='row'>
<div className='col-12'>
<h2>{ data.title }</h2>
<input
type='number'
value={data.impressions}
name='impressions'
onChange={e => handleChange(e)}
/>
</div>
</div>
)
}
However, running this code causes all keys in the data state to be overwritten (and thus my h2 tag displays nothing). It was my understanding that React State carried out a shallow merge, and so in this case would only update the impressions key:value?
Functional component's useState hook doesn't actually shallow merge state updates so you need to manage this yourself.
Use a functional state update to access the previous state to shallow copy into the next state.
Note
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};
});
You may want to also ensure you maintain your state invariant. Since you are updating your state via key-value pairs I'm assuming you are actually using an object.
const [data, setData] = useState({}); // <-- empty object initial state
const handleChange = (event) => {
setData(data => ({
...data, // <-- shallow copy existing state
[event.target.name]: event.target.value,
}));
}

Cannot read property 'length' of undefined in functional component while it works in class component [duplicate]

I'm finding these two pieces of the React Hooks docs a little confusing. Which one is the best practice for updating a state object using the state hook?
Imagine a want to make the following state update:
INITIAL_STATE = {
propA: true,
propB: true
}
stateAfter = {
propA: true,
propB: false // Changing this property
}
OPTION 1
From the Using the React Hook article, we get that this is possible:
const [count, setCount] = useState(0);
setCount(count + 1);
So I could do:
const [myState, setMyState] = useState(INITIAL_STATE);
And then:
setMyState({
...myState,
propB: false
});
OPTION 2
And from the Hooks Reference we get that:
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};
});
As far as I know, both works. So, what is the difference? Which one is the best practice? Should I use pass the function (OPTION 2) to access the previous state, or should I simply access the current state with spread syntax (OPTION 1)?
Both options are valid, but just as with setState in a class component you need to be careful when updating state derived from something that already is in state.
If you e.g. update a count twice in a row, it will not work as expected if you don't use the function version of updating the state.
const { useState } = React;
function App() {
const [count, setCount] = useState(0);
function brokenIncrement() {
setCount(count + 1);
setCount(count + 1);
}
function increment() {
setCount(count => count + 1);
setCount(count => count + 1);
}
return (
<div>
<div>{count}</div>
<button onClick={brokenIncrement}>Broken increment</button>
<button onClick={increment}>Increment</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
If anyone is searching for useState() hooks update for object
Through Input
const [state, setState] = useState({ fName: "", lName: "" });
const handleChange = e => {
const { name, value } = e.target;
setState(prevState => ({
...prevState,
[name]: value
}));
};
<input
value={state.fName}
type="text"
onChange={handleChange}
name="fName"
/>
<input
value={state.lName}
type="text"
onChange={handleChange}
name="lName"
/>
Through onSubmit or button click
setState(prevState => ({
...prevState,
fName: 'your updated value here'
}));
The best practice is to use separate calls:
const [a, setA] = useState(true);
const [b, setB] = useState(true);
Option 1 might lead to more bugs because such code often end up inside a closure which has an outdated value of myState.
Option 2 should be used when the new state is based on the old one:
setCount(count => count + 1);
For complex state structure consider using useReducer
For complex structures that share some shape and logic you can create a custom hook:
function useField(defaultValue) {
const [value, setValue] = useState(defaultValue);
const [dirty, setDirty] = useState(false);
const [touched, setTouched] = useState(false);
function handleChange(e) {
setValue(e.target.value);
setTouched(true);
}
return {
value, setValue,
dirty, setDirty,
touched, setTouched,
handleChange
}
}
function MyComponent() {
const username = useField('some username');
const email = useField('some#mail.com');
return <input name="username" value={username.value} onChange={username.handleChange}/>;
}
Which one is the best practice for updating a state object using the state hook?
They are both valid as other answers have pointed out.
what is the difference?
It seems like the confusion is due to "Unlike the setState method found in class components, useState does not automatically merge update objects", especially the "merge" part.
Let's compare this.setState & useState
class SetStateApp extends React.Component {
state = {
propA: true,
propB: true
};
toggle = e => {
const { name } = e.target;
this.setState(
prevState => ({
[name]: !prevState[name]
}),
() => console.log(`this.state`, this.state)
);
};
...
}
function HooksApp() {
const INITIAL_STATE = { propA: true, propB: true };
const [myState, setMyState] = React.useState(INITIAL_STATE);
const { propA, propB } = myState;
function toggle(e) {
const { name } = e.target;
setMyState({ [name]: !myState[name] });
}
...
}
Both of them toggles propA/B in toggle handler.
And they both update just one prop passed as e.target.name.
Check out the difference it makes when you update just one property in setMyState.
Following demo shows that clicking on propA throws an error(which occurs setMyState only),
You can following along
Warning: A component is changing a controlled input of type checkbox to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
It's because when you click on propA checkbox, propB value is dropped and only propA value is toggled thus making propB's checked value as undefined making the checkbox uncontrolled.
And the this.setState updates only one property at a time but it merges other property thus the checkboxes stay controlled.
I dug thru the source code and the behavior is due to useState calling useReducer
Internally, useState calls useReducer, which returns whatever state a reducer returns.
https://github.com/facebook/react/blob/2b93d686e3/packages/react-reconciler/src/ReactFiberHooks.js#L1230
useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
currentHookNameInDev = 'useState';
...
try {
return updateState(initialState);
} finally {
...
}
},
where updateState is the internal implementation for useReducer.
function updateState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
return updateReducer(basicStateReducer, (initialState: any));
}
useReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
currentHookNameInDev = 'useReducer';
updateHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateReducer(reducer, initialArg, init);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
},
If you are familiar with Redux, you normally return a new object by spreading over previous state as you did in option 1.
setMyState({
...myState,
propB: false
});
So if you set just one property, other properties are not merged.
One or more options regarding state type can be suitable depending on your usecase
Generally you could follow the following rules to decide the sort of state that you want
First: Are the individual states related
If the individual state that you have in your application are related to one other then you can choose to group them together in an object. Else its better to keep them separate and use multiple useState so that when dealing with specific handlers you are only updating the relavant state property and are not concerned about the others
For instance, user properties such as name, email are related and you can group them together Whereas for maintaining multiple counters you can make use of multiple useState hooks
Second: Is the logic to update state complex and depends on the handler or user interaction
In the above case its better to make use of useReducer for state definition. Such kind of scenario is very common when you are trying to create for example and todo app where you want to update, create and delete elements on different interactions
Should I use pass the function (OPTION 2) to access the previous
state, or should I simply access the current state with spread syntax
(OPTION 1)?
state updates using hooks are also batched and hence whenever you want to update state based on previous one its better to use the callback pattern.
The callback pattern to update state also comes in handy when the setter doesn't receive updated value from enclosed closure due to it being defined only once. An example of such as case if the useEffect being called only on initial render when adds a listener that updates state on an event.
Both are perfectly fine for that use case. The functional argument that you pass to setState is only really useful when you want to conditionally set the state by diffing the previous state (I mean you can just do it with logic surrounding the call to setState but I think it looks cleaner in the function) or if you set state in a closure that doesn't have immediate access to the freshest version of previous state.
An example being something like an event listener that is only bound once (for whatever reason) on mount to the window. E.g.
useEffect(function() {
window.addEventListener("click", handleClick)
}, [])
function handleClick() {
setState(prevState => ({...prevState, new: true }))
}
If handleClick was only setting the state using option 1, it would look like setState({...prevState, new: true }). However, this would likely introduce a bug because prevState would only capture the state on initial render and not from any updates. The function argument passed to setState would always have access to the most recent iteration of your state.
Both options are valid but they do make a difference.
Use Option 1 (setCount(count + 1)) if
Property doesn't matter visually when it updates browser
Sacrifice refresh rate for performance
Updating input state based on event (ie event.target.value); if you use Option 2, it will set event to null due to performance reasons unless you have event.persist() - Refer to event pooling.
Use Option 2 (setCount(c => c + 1)) if
Property does matter when it updates on the browser
Sacrifice performance for better refresh rate
I noticed this issue when some Alerts with autoclose feature that should close sequentially closed in batches.
Note: I don't have stats proving the difference in performance but its based on a React conference on React 16 performance optimizations.
I find it very convenient to use useReducer hook for managing complex state, instead of useState. You initialize state and updating function like this:
const initialState = { name: "Bob", occupation: "builder" };
const [state, updateState] = useReducer(
(state, updates) => {...state, ...updates},
initialState
);
And then you're able to update your state by only passing partial updates:
updateState({ occupation: "postman" })
The solution I am going to propose is much simpler and easier to not mess up than the ones above, and has the same usage as the useState API.
Use the npm package use-merge-state (here). Add it to your dependencies, then, use it like:
const useMergeState = require("use-merge-state") // Import
const [state, setState] = useMergeState(initial_state, {merge: true}) // Declare
setState(new_state) // Just like you set a new state with 'useState'
Hope this helps everyone. :)

useCallback necessary within React.memo?

Consider this example:
import React, { useCallback } from 'react';
type UserInputProps = {
onChange: (value: string) => void;
};
const UserInput = React.memo(({ onChange }: UserInputProps) => {
// Is this `useCallback` redundant?
const handleChange = useCallback(
(event) => {
onChange(event.target.value);
},
[onChange]
);
return <input type="text" onChange={handleChange} />;
});
export default UserInput;
My questions are:
When the props consist of only onChange and no other elements, is the useCallback unnecessary in this case, since the entire component is already memo-ed based on onChange?
If we add an additional prop (say a value for the initial value for the <input>), then I think useCallback becomes useful, since otherwise handleChange will be recreated even if onChange doesn't change but value is changed. Is that correct?
When the props contains only onChange and no other elements, is the useCallback unnecessary in this case, since the entire component is already memo-ed based on onChange?
It's memoized based on all props, not just onChange. Yes, useCallback is unnecessary there.
If we add an additional prop (say a value for the initial value for the <input>), then I think useCallback becomes useful, since otherwise handleChange will be recreated even if onChange doesn't change but value is changed. Is that correct?
If you want to update value and not update onChange when updating the input, you'd add value to your props and continue to use useCallback for your handleChange (if you like, so that React doesn't have to swap in a new event handler when it's setting the value; my impression is that's often overkill). E.g.:
const UserInput = React.memo(({ onChange, value }: UserInputProps) => {
const handleChange = useCallback(
(event) => {
onChange(event.target.value);
},
[onChange]
);
return <input type="text" value={value} onChange={handleChange} />;
});
That will use the latest value and reuse the previous handleChange if the onChange prop hasn't changed.
You might not keep React.memo in that case, if you're expecting that value will get changed as a result of the onChange. That is, if you expect the common case to be that value will be different each time your component is called, then React.memo's check of the props is extra work you might not keep:
const UserInput = ({ onChange, value }: UserInputProps) => {
const handleChange = useCallback(
(event) => {
onChange(event.target.value);
},
[onChange]
);
return <input type="text" value={value} onChange={handleChange} />;
};
But you can keep it if you want React.memo to keep checking the props and not call your function when neither prop changes.
If you just want to supply the initial value to your component and then control it locally, you could continue to use React.memo (since it still renders the same thing for the same input props), in which case you don't need useCallback:
const UserInput = React.memo(({ onChange, initialValue }: UserInputProps) => {
const [value, setValue] = useState(initialValue);
const handleChange = (event) => {
setValue(event.target.value);
onChange(event.target.value);
};
return <input type="text" value={value} onChange={handleChange} />;
});
That will only get called when onChange or initialValue changes. You could use also useCallback there in order to, again, only update the onChange on the input when the onChange prop changes, to avoid having React remove the old handler and set the new one when just the value changes:
const UserInput = React.memo(({ onChange, initialValue }: UserInputProps) => {
const [value, setValue] = useState(initialValue);
const handleChange = useCallback(
(event) => {
setValue(event.target.value);
onChange(event.target.value);
},
[onChange]
);
return <input type="text" value={value} onChange={handleChange} />;
});
Side note: One thing to remember is that a new handleChange function is created every time your component function is called, even when you're using useCallback. It has to be, so it can be passed into useCallback as an argument. The only difference is whether you use that new function, or the original one that was created the first time (the result of useCallback). I think the reason for reusing the first one that was created for a given set of dependencies is to minimize changes passed to child components.
When the props consist of only onChange and no other elements, is the useCallback unnecessary in this case, since the entire component is already memo-ed based on onChange?
No, it might be necessary, memo and useCallback don't serve the same purpose.
Without useCallback you may have "heavy computation" on every render:
"heavy computation" refers to a generic case and not to this specific example where you only pass the event's value.
const UserInput = React.memo(({ onChange = () => {} }) => {
// Uncomment for memoization
// Note that you can implement useCallback with useMemo
// const handleChange = useMemo(() => {
// console.log("heavy computation memoized");
// return event => {
// onChange(event.target.value);
// };
// }, [onChange]);
const handleChange = event => {
// Here we can have some heavy computation
// Not related to this specific usecase
console.log("heavy computation on every render");
onChange(event.target.value);
};
return <input type="text" onChange={handleChange} />;
});
If we add an additional prop (say a value for the initial value for the ), then I think useCallback becomes useful, since otherwise handleChange will be recreated even if onChange doesn't change but value is changed. Is that correct?
If you are going to use controlled component (due to use of value prop of input), the initialValue will be initialized once, so it pretty useless trying to memorize it (for what purpose?):
const UserInputWithValue = ({ onChange, initialValue }) => {
// initilized once, no need to memoize
const [value,setValue] = useState(initialValue);
const handleChange = useCallback(
(event) => {
setValue(event.target.value)
onChange(event.target.value);
},
[onChange]
);
// Controlled
return <input type="text" value={value} onChange={handleChange} />;
};

Best approach for using same component for editing and adding data. Mixing component state with redux store

I'm building web app in React with Redux. It is simple device manager. I'm using the same component for adding and updating device in database. I'm not sure, if my approach is correct. Here you can find parts of my solution:
UPDATE MODE:
In componentDidMount I'm checking, if deviceId was passed in url (edit mode). If so, I'm calling redux action to get retrieve data from database. I'm using connect function, so when response arrives, It will be mapped to component props.
Here is my mapStateToProps (probably I should map only specific property but it does not matter in this case)
const mapStateToProps = state => ({
...state
})
and componentDidMount:
componentDidMount() {
const deviceId = this.props.match.params.deviceId;
if (deviceId) {
this.props.getDevice(deviceId);
this.setState({ editMode: true });
}
}
Next, componentWillReceiveProps will be fired and I will be able to call setState in order to populate inputs in my form.
componentWillReceiveProps(nextProps) {
if (nextProps.devices.item) {
this.setState({
id: nextProps.devices.item.id,
name: nextProps.devices.item.name,
description: nextProps.devices.item.description
});
}
}
ADD MODE:
Add mode is even simpler - I'm just calling setState on each input change.
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
That's how my inputs looks:
<TextField
onChange={this.handleChange('description')}
label="Description"
className={classes.textField}
value={this.state.description}
/>
I don't like this approach because I have to call setState() after receiving data from backend. I'm also using componentWillReceiveProps which is bad practice.
Are there any better approaches? I can use for example only redux store instead of component state (but I don't need inputs data in redux store). Maybe I can use React ref field and get rid of component state?
Additional question - should I really call setState on each input onChange?
To avoid using componentWillReceiveProps, and because you are using redux, you can do:
class YourComponent extends React.Component {
state = {
// ...
description: undefined,
};
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.description === undefined && nextProps.description) {
return { description: nextProps.description };
}
return null;
}
componentDidMount() {
const deviceId = this.props.match.params.deviceId;
if (deviceId) {
this.props.getDevice(deviceId);
this.setState({ editMode: true });
}
}
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
// ...
render() {
let { description } = this.state;
description = description || ''; // use this value in your `TextField`.
// ...
return (
<TextField
onChange={this.handleChange('description')}
label="Description"
className={classes.textField}
value={description}
/>
);
}
}
const mapStateToProps = (state) => {
let props = { ...state };
const { devices } = state;
if (devices && devices.item) {
props = {
...props,
id: devices.item.id,
name: devices.item.name,
description: devices.item.description,
};
}
return props;
};
export default connect(
mapStateToProps,
)(YourComponent);
You can then access id, name, and description thought this.props instead of this.state. It works because mapStateToProps will be evaluated every time you update the redux store. Also, you will be able to access description through this.state and leave your TextField as is. You can read more about getDerivedStateFromProps here.
As for your second question, calling setState every time the input changes is totally fine; that's what's called a controlled component, and the react team (nor me) encourage its use. See here.

Categories

Resources