In ReactJS i am getting this issue while rendering - reactjs

The first image is about the error im getting and the second one is the API from which I need to show only vendor: store_name but while clicking on the update button it shows this error otherwise it remains okay and reloading manually only it works.
Unhandled Rejection (TypeError): Cannot read properties of undefined (reading 'store_address')
let cart_product_item = this.props.cart_product;
return (
{
cart_product_item && cart_product_item.length > 0 ?
cart_product_item[0].vendor.store_address : null
}
)
const mapStateToProps = (state) => {
return {
cart_product: state.homepageData.cart,
};
};
export default connect(mapStateToProps, {myCart})(Checkout);

The question does not provide all the information required to answer, but here is probably what happens:
You render a component
The component needs data from an API, and the data does not arrive immediately. The component renders before the data arrives.
Your component assumes the data is there immediately during first render, and thus throws an error when trying to access a property of an undefined object.
If the problem is indeed this, it can be overcome in many ways:
Make it appear loading if it does not have the data and when it does, display the data. This can be achieved e.g. by cart_product_item?.[0]?.vendor?.store_address || "Loading" or similar.
Do not render the component (= do not return anything other than e.g. null if the data is not there yet.

Related

ReactJS: Props not loading on initial render. Cannot use map on undefined variable [duplicate]

This question already has answers here:
An error comes when mapping array of objects in React
(3 answers)
Closed 1 year ago.
I have a complex react application with many components.
The first thing is when the user logs in, I fetch the information from the database and store it in a global state, the child components then get the value using props and then I load the relevant data.
This is what a component looks like:
const ProfileForm12 = (props) => {
const { profile } = props
...
...
profile.map(x=>console.log(x))
}
export default ProfileForm12
Here I initially I get an error that crashes the application, because the profile is null on initial render, but on the second render, the data loads.
How can I solve this issue without using useState or useEffect because the application already uses a lot of state and using them are causing too many re-renders.
I tried putting this inside an if loop, but then the data wont load on the frontend.
UPDATE: All the suggestions below worked. I will take some time to see which one works well with my application but thank you all for your answers :)
I think you can keep one null check before map method to get rid of error.
profile?.map(x=> console.log(x));
If loading/initializing your app is a requirement for all your components, you should have some "Loading..." screen that identify the loading state, shows the loading screen, and once it's loaded. render the full app.
Protecting from null all over your application is not so productive.
You want to avoid rendering the child component when the data is not set, so a ternary operator (or logical expression) should ensure that
const ParentComponent = () => {
...
return (
<>
...
{propsToSend && (<ChildComponent myprop={propsToSend}/>)}
</>
)
}
This way you guarantee that the child component only renders if the props are defined.

access to the endpoints wp api rest title post rendered inreact app

I cracking my head against the wall, I'm developing a React App with wordpress as an API (wp rest API). everything is working well, I'm fetching and rendering all the post but when I go to the single post I can't fetch and render the title and content because it's said {rendered: "dwdwdf"} and I do the {post.title.rendered} as I do in the postlist component to get the title and works but in the single post doesn't.
To make it more clear here is the code:
const id = this.props.match.params.id;
let postUrl = `http://thesite.com/wp-json/wp/v2/posts/${id}`;
fetch(postUrl)
.then(data => data.json())
.then(data => {
this.setState({
item: data
})
})
when I console the this.state.item.title it shows me this:
{rendered: "dwdwdf"}
rendered: "dwdwdf"
__proto__: Object
it should be render as I do in my postlist component, like this {this.state.item.title.rendered} and done ! but not, it gave me this error:
TypeError: Cannot read property 'rendered' of undefined
I checked the api rest wp documentation and a lot of blogs and stackoverflow but I can't find anything that helps me.
if someone can guide me, thanks.
In principle, what you have shown should work. The response of that WP API endpoint does include:
"title": {
"rendered": "Your post's title"
},
This is backed up by your console.log output.
You haven't shown your default state, where your console.log output is written, or where you are trying to access the full path (i.e. {this.state.item.title.rendered}), but it sounds like you are doing at least the full path part in the render function.
Assuming so, I believe what you have is a timing issue. The render function may run as many times as it needs to (i.e. when the component updates). The first time it runs, your state does not have the title property yet, as your HTTP request is not yet complete. It's undefined, and when you try to access a child property of undefined, you get that error.
The difference with your earlier console statement is you aren't trying to access a property of undefined. You are just outputting the state's value itself (i.e., undefined). And one very tricky thing about the console is that it's not a historical record. A value that a console.log shows can change...say from 'undefined' to the value that gets set there later, title property and all. It all happens so fast that you don't see this.
It's best to keep in mind that the render() function may run over and over again, and your JSX needs to be written in such a way that it accounts for the possible states you expect. Here, you can expect that initially your state for "item" does not have all the properties that it will have later.
You could instead write something like,
{this.state.item.title ? this.state.item.title.rendered : 'Loading...'}
Or whatever else you'd like to write there (or leave it blank, etc.). So you are first checking to see if title has a truthy value (not undefined), and if so, accessing the child's property. A common pattern is to use this form:
{this.state.item.title && (<h1>{this.state.item.title.rendered}</h1>)}
Here the difference is we are relying on JavaScript's return value for &&, which will be the second item's value. If the first item is falsy, it doesn't even look at the second part, so it doesn't complain.
Or, you may try to take a step back and track a separate variable in state for your loading process. Something like dataReady, which you would set to true once you receive the HTTP request back. Then, your render code looks a bit cleaner:
{this.state.dataReady && (
<h1>{this.state.item.title.rendered}</h1>
<h2>(some other stuff</h2>
)}
For more information, I recommend you read about conditional rendering.
I'm just guessin but this may be due to your state not having the needed initial conditions to render correctly, since you are loading the info asynchronously.
For example, if your intial state is like this:
this.state = {
item: {},
...anyOtherAttributes
}
and you try to render this.state.item.title.rendered, it won't find title so it'll be undefined and then React won't be able to get the rendered property correctly.
To fix it you should initialize your posts like:
this.state = {
item: {
title: {rendered: ''}
},
...anyOtherAttributes
}
so it has kind of a placeholder to render while waiting for the aynchronous call to end and update the state.
Hope it helps.

Passing data based on another api response from redux

I have an api which will list what all access do the current user have, so once the app.js loads I am calling the api in the componentWillMount, so basically I have three routes , home, userslist, eachuser. So the home is a static text page.
userslist is a component where I list all the users, once you click on the edit icon of user it will take you to the details of the user in the eachuser component.
The problem is since the calls are async once the useraccessApi resolves and gets only the data I should call the usersListApi , by passing the useraccessApi response.
What I mean by happy flow is first user loads the localhost:3000/home so the useraccessApi will call and the redux have data, so while switching to userslist tab on componenWillMount it will work. But if the user directly selects localhost:3000/userlist it will throw error on componenWillMount so moved the code to componentWillRecieveProps().
So how can I resolve this issue. Or should I use mergeProps to resolve it.
App.js
componenWillMount(){
this.props.userAccessApi()
}
UsersList
componentWillMount(){
const {currentUserAccess} = this.props
// if its a happy flow else it will be currentUserAccess is undefined
if(currentUserAccess){
this.props.usersListApi(currentUserAccess)
}
}
// inorder to resolve it
componentWillRecieveProps(nextProps){
const {currentUserAccess} = nextProps
if(currentUserAccess){
this.props.usersListApi(currentUserAccess)
}
}
const mapStateToProps = (state) => {
return {
currentUserAccess: state.access
}
}
This is the expected behavior of React Lifecycle events.
For componentWillMount
This function is called right before the component’s first render, so at first glance it appears to be a perfect place to put data fetching logic.
But, there’s a “gotcha,” though: An asynchronous call to fetch data will not return before the render happens. This means the component will render with empty data at least once. Which is why your dispatch function fails and you get undefined.
Instead you should use ComponentDidMountas the event in which you will fetch data.
By the time componentDidMount is called, the component has been rendered once.
In practice, componentDidMount is the best place to put calls to fetch data.
Using it makes it clear that data won’t be loaded until after the initial render. This reminds you to set up initial state properly, so you don’t end up with undefined state that causes errors.

How to clear error message coming from redux actions

I have an API, which may return an error. In the simplistic form, the component reads as below. The question is, when an error occurs, what is the steps to dismiss/clear the error message?
class LoginError extends Component {
render() {
return (
{
this.props.error != null ?
<h2>this.props.error</h2> :
undefined
}
)
}
}
const mapStateToProps = (state) => {
return {
error: state.error
};
}
export default connect(mapStateToProps, null)(LoginError);
There is no straight forward way to do this, you basically have two options: set the error to undefined in the reducer when another action fires, or provide a close button on the error itself using a reusable error component, that dispatches an action that will set the error to undefined in the reducer.
Personally i've always used the first method, lets take the submit of a form as an example. When the user submits the form you fire form-submit/request and maybe show a loading message. If the form is sent correctly you fire form-submit and if an error happens you fire form-submit/error, setting the error in the reducer. Then in form-submit/request you clear the error, so the user gets feedback if an error happens but if the form is submitted again the error is cleared. If you don't want to do anything when the form is submitted, which is odd, you can clear the error when form-submit is fired. The downside of this approach is that if for example you want to clear the error when any field of the form is changed, you'll have to add another action and make the error undefined for that action as well.
Now if you put a close button in the error component you can reuse the error React component, but you'll have to have a action/dismiss-error action for every API call and set the error to undefined on the reducer for each one of them, this can get very tedious quickly.
The best approach for me is to use the first method, but choosing carefully how much errors you display for each page or section. This way each page can have its error section that will be displayed for any API call that is associated with the page and you only need an error action for each page, not for every API call.
Quote from docs:
A store holds the whole state tree of your application. The only way
to change the state inside it is to dispatch an action on it.
That's it. If you keep something in redux state then to change it's value you have to dispatch an action.

Meteor handle.ready() in render() not triggering rerender of component

I have the following code in my render method:
render() {
return (
<div>
{this.props.spatulaReady.ready() ? <p>{this.props.spatula.name}</p> : <p>loading spatula</p>}
</div>
)
}
Which according to my understanding, checks if the subscriptionhandle is ready (data is there) and displays it. If no data is available, it should display a simple loading message. However, when I first load the page this snippet is on, it get's stuck on the loading part. On a page reload the data (usually) displays fine.
If I check the spatulaReady.ready() when the page first loads and while the display is stuck on 'loading spatula', and the data that should be there, the handle reports as ready and the data is there like it is supposed to be. If I refresh the page it all displays fine as well. The problem is, this way of checking for data and rendering if it has arrived has worked fine for me in the past. Is it because the render method is not reactive? Because handle.ready() should be reactive.
What makes it even weirder is that it sometimes DOES correctly display the data on page load, seemingly at random.
CreateContainer code:
export default createContainer(props => {
return {
user: Meteor.user(),
spatulaReady: Meteor.subscribe('spatula.byId', props.deviceId),
spatula: SpatulaCollection.findOne()
}
}, SpatulaConfig)
Publication code:
Meteor.publish('spatula.byId', function(deviceId) {
const ERROR_MESSAGE = 'Error while obtaining spatula by id'
if (!this.userId) //Check for login
throw new Meteor.Error('Subscription denied!')
const spatula = SpatulaCollection.findOne({_id: deviceId})
if(!spatula) //No spatula by this id
throw new Meteor.Error(403, ERROR_MESSAGE)
if(spatula.ownedBy != this.userId) //Spatula does not belong to this user
throw new Meteor.Error(403, ERROR_MESSAGE)
return SpatulaCollection.find({_id: deviceId})
})
I know I'm missing a piece of the puzzle here, but I've been unsuccessful at finding it. If you don't know the solution to my specific problem, pointing me in the right direction with another way of waiting for the data to arrive before displaying it is also greatly appreciated.
EDIT: After doing some trial-and-error and reading various other posts somewhat related to my project, I figured out the solution:
export default createContainer(props => {
const sHandle= Meteor.subscribe('spatula.byId', props.deviceId)
return {
user: Meteor.user(),
spatulaReady: sHandle.ready(),
spatula: SpatulaCollection.findOne()
}
}, SpatulaConfig)
It still makes no sense to me that moving the ready() call to create container fixed all my problems though.
As you figured out, moving the .ready() call to createContainer fixes the problem. This is because Meteor reactivity only works when you call a reactive data source (a reactive function), such as collection.find() or subscriptionHandle.ready() within a reactive context, such as Tracker.autorun or createContainer. Functions within the React component, including render, are not reactive contexts from Meteor's perspective.
Note that React and Meteor reactivity are two different things. React's reactivity works simply so that whenever a component's props or state change, it's render function is re-run. It does not understand anything about Meteor's reactive data sources. Since createContainer (that is re-run by Meteor reactivity when reactive data sources in it change) simply passes props to the underlying component, the component is re-rendered by React when the props given from createContainer change.

Resources