React get value of object from localstorge - javascript

I get data from localstorage.
I used useEffect. But when I want to get a value in object, I got problem.
const [carinfo, setCarInfo] = useState();
useEffect(() => {
const storedData = JSON.parse(localStorage.getItem("carData"));
setCarInfo(storedData);
console.log(storedData.brand);
}, []);
console.log(carinfo.brand);
console.log(storedData.brand) is working.
No console.log(carinfo.brand) is not working
I have this error
Uncaught TypeError: Cannot read properties of undefined (reading 'brand')

I suspect you get the error because the effect hasn't happened yet. The first time your component renders, carinfo is undefined.
You can set an initial value to state to get around this:
const [carinfo, setCarInfo] = useState({});

Your console.log(carinfo.brand) is getting executed before the useEffect.
That means you're trying to access the value of carinfo before setting it.
Sol 1: Put the console.log(carinfo.brand) inside useEffect
Sol 2: If you need the carinfo outside useEffect, write it inside a function and call that function after setting the carinfo value

On your initial render, the value of carinfo will match what you call useState with.
useState() is the equivalent of useState(undefined). undefined is a value without a brand property.
useEffect will fire and then update the state with whatever value you have in localStorage, given that it matches your expected data structure (you may want to consider creating a safer way get to localStorage.
Nevertheless,only after the initial render is the state set, and then you may have the expected value in carinfo.
You can use optional chaining to protect yourself, console.log(carinfo?.brand) or some other way to handle the undefined case.

The other answers cover the reasons of the Exception, this is just the simplest fix:
console.log(carinfo?.brand);

Related

How do I resolve Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop while setting a state?

I am getting an object location from another page like this.
const location = useLocation();
I am trying to set a variable like this.
if (location.state !== null) {
console.log(location.state.name.companyName)
setcompanyOneAlready(location.state.name.companyName);
console.log(companyOneAlready);
}
But this results in a "Too many renders" error. I realize it's because of the way I am setting companyOneAlready but I don't know how to correct this. Any help is appreciated.
It seems you are running the code as an unintentional side effect and the condition is malformed.
Use the useEffect hook with correct dependencies to issue the side-effect to update state.
Example:
useEffect(() => {
if (location.state !== null) {
setcompanyOneAlready(location.state.name.companyName);
}
}, [location.state]);
To help save a rerender you can also check the current companyOneAlready value prior to enqueueing the state update.
useEffect(() => {
if (location.state?.name?.companyName !== companyOneAlready) {
setcompanyOneAlready(location.state.name.companyName);
}
}, [location.state, companyOneAlready]);
It seems like location.state is never null. I believe by default it is an empty object and {} !== null is always true. Also, I assume that setcompanyOneAlready changes the state in the component. That triggers rerender and your's condition will be true on every render. That's the reason why you get the mentioned error.
You should put this condition inside useEffect and useEffect should have a name or companyName in the dependency array to rerun the effect only when some of these values are changed.

How Can I access a property of an object array set in state

I am building my first react app for my final react course on scrimba and I am retrieving data from an API and setting a part of the data to an array set in state, but I cant seem to access a property of the array
import React from 'react'
const [request, setRequest] = React.useState([])
React.useEffect (() => {
fetch('https://opentdb.com/api.php?amount=5')
.then(res => res.json())
.then(data => setRequest(data.results.map(({ question, correct_answer, incorrect_answers }) => ({question, correct_answer, incorrect_answers}))))
}, [])
console.log(request[0]) // returns the first object array
console.log(request[0]["question"]) // gives an error
console.log(requst[0].question) // gives an error
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
but I cant seem to access a property of the array
Sure you can, but only when that array element exists. The initial state of the array is empty:
const [request, setRequest] = React.useState([])
An empty array has no elements, so this logs undefined to the console:
console.log(request[0])
And then this produces an error:
console.log(request[0]["question"])
And then this does not produce an error, because it isn't executed, because the line before it produced an error:
console.log(requst[0].question)
But if that line of code was executed, it too would produce an error.
The data you're fetching to populate state is coming from an asynchronous operation. So it's going to happen at some later time. (Could be milliseconds and feel instantaneous, but still some later time.)
Until that data has been fetched and until the state has been updated, the code needs to be able to handle the initial state of the empty array. Optional chaining is one option:
console.log(requst[0]?.question)
Or perhaps wrapping the whole thing in a conditional:
if (request.length > 0) {
console.log(request[0]);
console.log(request[0]["question"]);
console.log(requst[0].question);
}
How you prefer to handle an empty array is up to you and the logic you're implementing. But the point is that since you define an empty array, the code should expect to start with an empty array.
Setting data inside request state takes time. So you always get undefined for the first log of console.log(request[0]) and error for other two console.logs.
In that case, you can check by using UseEffect hook.
useEffect(() => {
if(request.length === 0) return;
console.log(request[0]); // returns the first object array
console.log(request[0]['question']);
console.log(requst[0].question);
}, [request]);
If you don't want to use useEffect, you can use ? to access object properties without error.
console.log(request[0]);
console.log(requst[0]?.question);
if (request !== undefined && request.length > 0) {
console.log(request[0])
console.log(request[0]["question"])
console.log(request[0].question)
}

Error when using setState to set an empty array to object property ( react Hooks)

im having problems with this line:
useEffect(() => {
if (AplicationsList) {
setDetail({ ...Detail, components: AplicationsList });
}
}, [AplicationsList]);
When AplicationsList its an empty array [] it gives me an error, like Detail is then undefined. But when its not empty works fine...
either if the array its empty or not I have to set the value on components attribute of Detail.
Any idea why this is happening?
This is the error I'm facing in the child component that depends on "Detail" passed as props:cannot read property map of undefined
And I get this react warning:
can't perform a react state update in an unmounted component
I've tried with this functional update but still is giving me the same errors:
setDetail(Detail => ({
...Detail,
components: AplicationsList,
}));
From you question, I understand Details is an object and AplicationsList is an array.
I think you have set the state of details this way or similar to this
const [Details, setDetails] = useState();. The problem with this is the initial value of Details is undefined and you are trying to destructure undefined. Instead set it as const [Details, setDetails] = useState({});
In useEffect you are checking if AplicationsList is empty, but if(AplicationsList) return true even for empty array. So check for the length instead, if(AplicationsList.length > 0)
Suggestion: It's good practice to have the state variable name to be of lowerCase and follow camleCase format to name variable. Instead of Details and ApplicationsList, its good you rename to details and applicationsList.

React.js Passing setState as prop causes a warning about using props for dependencies

I am passing state as a variable down to a component via props like to...
const [someState, setSomeState] = useState([])
PlaceDataInIndexDB({ setSomeState: setSomeState,
user: props.user })
And in the PlaceDataInIndexDB.js I have a useEffect which eventually sets the state using
useEffect(() => {
props.setSomeState([array])
}), [props.user]
The issue is that I get a warning saying I need to use props in my dependency array, however, I do not want the useEffect to run every time props change because I have a lot of other props in there. What can I do for this? This is an extremely simplified version of my code. i can post exact code but it is a lot more to look through.
And here is the warning...
React Hook useEffect has a missing dependency: 'props'. Either include
it or remove the dependency array. However, 'props' will change when
any prop changes, so the preferred fix is to destructure the 'props' object outside of the useEffect call and refer to those specific props
inside useEffect react-hooks/exhaustive-deps
It's telling you what the issue is. Generally, anything referenced inside of the useEffect function needs to also exist in the dependency array, so React knows to run it again when those things change. If the thing you're using is a property of some other object (like props), it's best to pull the values out of there prior to referencing them.
const PlaceDataInIndexDB = (props) => {
const { setSomeState, user } = props
useEffect(() => {
setSomeState([array])
}), [setSomeState, array] // <-- your example doesn't show where `array` comes from, but needed here as well
// ...
}
You can, in fact, destructure the props inline so that you never even have a reference to the entire props object:
const PlaceDataInIndexDB = ({ setSomeState, user }) => {
Note that setSomeState must also be in the dependency array. If -- and only if -- the useState is in the same component, the linter is smart enough to know that it never changes and lets you leave it out. However, if it's passed in as a prop from somewhere else, there is no way for it to know.
The linting rules for hooks are very good. By and large, unless you really know what you're doing, you can blindly follow the suggestions it makes. Eslint actually added an entire "suggestions" feature for this specific rule, which e.g. vscode will change for you if you put your cursor on the error and press ctrl+.

Access field of object in array (state) in React

In React component I have a state called projectTypes which is an array of objects:
const [projectTypes, setProjectTypes] = useState([])
This state is set inside an useEffect hook:
useEffect(() => {
...
setProjectTypes(...)
...
})
with information received from a fetch to the server. That information is displayed inside a table and everything is working as expected.
I want to check a field inside the first element (an object) of the state array:
projectTypes[0]._id
To be sure that I'm accessing projectTypes when it already has values, I'm using another hook:
useEffect(() => {
...
console.log(projectTypes[0]);
console.log(projectTypes[0]._id);
...
}, [projectTypes]);
I have 2 questions:
Is this the right procedure to be sure that when accessing the state it already has values inside: creating a new hook with [projetTypes] as the 2nd parameter?
Inside this second hook, if I only use the first console.log - console.log(projectTypes[0]) - everything works as expected and the first object of the state is printed to the console with an _id field. But using the second one - console.log(projectTypes[0]._id) - results in an error: "TypeError: Cannot read property '_id' of undefined". Why can I access the object inside the state array just fine, and print it with all its fields, but if I try to access one of its fields, I get an error?
As the effect is called whenever any of its dependencies update, You can do it with as single effect.
within the effect, check if data within it is valid, if yes, do something, else continue.
like
useEffect(() => {
if(projectTypes && projectTypes.length >0){
//do something
}
}, [projectTypes]);

Categories