Axios response not available in nested React component - reactjs

Thanks for reading. I know this is going to seem over-simplified, but I am really stumped as to why this behavior is happening. I am calling an API using axios, getting the answer back, and passing that answer to the component below via props. When I let the following code execute, the console shows the object - populated with the server/api data.
class Player extends React.Component{
constructor(props) {
super(props);
}
render() {
console.log(this.props.personData)
return ...
}
}
If I add a field: console.log(this.props.personData.first_name) I get an error that personData is null, can't access its members. Thought that was weird, so stepped back to test and stumped myself further:
If I set a breakpoint in Chrome at the console.log line and instead type this.props.personData into console I get null. If I then step over the console.log line, it outputs null. But if I remove the breakpoint, that same line of code outputs a data-populated object.
Can anyone explain this behavior?

Assuming your component is rendered regardless of if Axios has returned yet, the personData prop will be null (or whatever you construct it in the parent's state) until Axios returns the async call.
You can either conditionally render the Player component once the data returns, or don't reference attributes of the data until you know it's not null.
This is because the Player component will be rendered initially when the parent mounts, however, the Axios API call will be async so the data isn't available.
Also, if the Player component is rendering attributes of personData without checking that they exist, it will throw and error and break the rendering cycle.
As to your question about breakpoint / logging differences, it's because the console log is mapped to the memory location of the prop, so eventually it is set and will display in the log. But, if you check by a breakpoint when you initially render it, the value will be null, and it will throw the above mentioned error.

HOw you are passing data to the component using redux or flux.

Related

React, componentWillReceive lifeCycle Hook not being invoked (on passing props)

I was trying to pass props both in App component (The root Component) and then to Header component from App itself.
I have used Life Cycle Hooks upto componentWillReceiveProps() in both App and Header Component.
componentWillMount(), render(), componentDidMount() are getting executed in both the Components in an expected order.
However, componentWillReceiveProps() is not executed at all even on passing props. This is a unExpected behaviour, since componentWillMount() was executed normally!
I shall be extremely thankful to know why is this happening ! Thank you :)
Kindly check the code sample from the below link:
https://codesandbox.io/s/r092xkpwjp
Please Note: Question has been updated, and it shows both scenarios now, new props being passed (in Header Component which works fine now) and no new props being passed as was previously the case in the question (in App Component which still shows why things were working unexpectedly).
I don't see why you expect your components to be updated as the The props being passed to them always stay the same and no new props have been passed to these at all, but generally you should use componentDidUpdate(prevProps, prevState).
componentWillReceiveProps() only gets invoked when the props passed to them are new props, different from the previous values. In the question this was not happening.
Note: The question has been updated now for it to work properly.
Also consider managing the state by static getDerivedStateFromProps(props, state), i.e.:
static getDerivedStateFromProps(props, state){
// just return the state
return {
isLoading: false,
money: props.money
}
}
- it's executed on init + on updates.
As stated in the official documentation (see https://reactjs.org/docs/react-component.html) the
componentWillReceiveProps()
lifecycle method is deprecated and should be avoided.
The reason why it is never called is that, to help user avoid it, React developers renamed it
UNSAFE_componentWillReceiveProps()
You should however avoid it, since they plan to deprecate that method
1.The main reason why the componentWillReceiveProps() was not being invoked was because my props passed to Header Component were not changing at all, I have been passing the same props again and again. And componentWillReceiveProps() gets executed only when the props being passed are different each time.
This is what my Header component looked at the time of asking this question.
<Header
menus={["home", "about", "services", "blog"]}
/>
The Header Component had only the menus prop as an array (at the time of asking this question) and no event was updating this menus prop , that's the reason why my
componentWillReceiveProps() was never invoked/called in the Header Component.
Note: To simply the things I now passed another prop to Header Component, and started to test my code on this prop instead of working with the array menus prop.
<Header
menus={["home", "about", "services", "blog"]}
prop={this.state.prop}
/>
And I am making my state.prop to update using an event handler:
// Dynamically sending Props
handlePropSending = () => {
this.setState({
...this.state,
prop: this.state.prop + 1
});
};
And as I am clicking on the button 'Sending Props to Header', the new props are sent to Header Component and our componentWillReceiveProps is being called and executed succefully.
And same was the issue with componentWillReceiveProps in the App Component
ReactDOM.render(, rootElement);
Since I was adding money prop (money={9199}), there actually is no Parent Component available for money prop to update it's value (which we could have passed to the App Component for it to invoke its componentWillReceiveProps method).
NOTE: The code is updated now and there are event handlers now to make sure the props keep updated and now the componentWillReceiveProps() method is indeed being successfully invoked.
componentWillReceiveProps demonstrated along with other life Cycle Hooks
LIKE THEY SAY :) ALL IS WELL THAT ENDS WELL :)

Setting initial state from props

I am trying to set an initial state for a component, from a prop that I have passed through. The prop is passed from App.js where it is populated from an API call.
When I check for a value for the component state there is none. How could this be?
Here is code setting initial state from ListBooks.js
class ListBooks extends Component {
constructor(props) {
super(props);
this.state = {
x: props.books
};
console.log("Check if state has been set")
console.log(this.state.x)
}
Here is where prop is populated in App.js
class BooksApp extends React.Component {
state = {
books: []
}
componentDidMount() {
console.log("Fetching All Books from API")
BooksAPI.getAll().then((books) => {
this.setState({books})
})
}
My overall goal is to be able to update the local state in the ListBooks component - so it updates immediately (haven't finished the code for this part) and then to update the server in background via API. To save a long reload after each update.
You shouldn't do this for sure.
First of all read this tutorial in the docs. It will show you the right way to organize your state.
If you can get values which you need from props - you shouldn't keep it in state - just use props in render.
My overall goal is to be able to update the local state in the ListBooks component - so it updates immediately (haven't finished the code for this part) and then to update the server in background via API. To save a long reload after each update.
This is not a good idea because you need to be sure that data is changed on the server sucessfully otherwise you will display not reliable data which can be confused for user.
For example: Let's imagine you have a list of books which you got from a server. Then user add new book to this list and you update state immediately and then send request to the API and something went wrong there (you have sent invalid data, for example). After that user decided to change the info of this newly added book but when he/she will try to do it - server will, probably, return 404 because there is no such book. It's kinda confusing, isn't it? Does it makes sense? I hope so.
Good luck and enjoy your coding :)
console.log(this.state.x) is undefined since the following code:
componentDidMount() {
console.log("Fetching All Books from API")
BooksAPI.getAll().then((books) => {
this.setState({books})
})
}
Includes two asynchronous calls that are not guaranteed to complete before it is rendered: BooksAPI.getAll() and setState.
But as the second comment to your question indicated, it is strange that you set the state in ListBooks from the passed props. The point in passing props is not to need to set local state in the component that gets the props.
If, as you wrote in the 4th comment on the question, you modify it inside ListBooks, then you probably need to call BooksAPI inside ListBooks and not pass props.

Setting Props on Imported Component via API

Have searched for a solution to this [seemingly] simple problem, but being relatively new to Node, haven't quite figured it out.
I need to set a prop on an imported Component prior to it rendering. The value for the prop is dynamic and based on the response of an API.
Based on what I've read, this should occur in the Constructor of my umbrella "App", but have few issues:
1) the Component throws an error when the prop value is set by API
2) I think this is because React attempts to render the Component before the API call completes
The Component renders without error if I manually set a valid value.
So, wondering how to grab the value and set the prop before the Component renders to avoid the error.
I can post a code sample, but have tried so many variations I'm not sure they'd be that useful.
Let me know if I am misunderstanding this.
You have two components, Component Parent and Component Child.
Component Parent gets information from an API, and sends it to Component Child.
Component Child renders depending on what you Parent sends to it
So, I believe the problem is that Child is not waiting for the API call to finish to render.
If I make an API call, I am not going to have the information for it immediately. You might want to handle the case where Child does not have the prop. To do that, you can call Child like this:
class Parent extends React.Component {
// Make the request to the API and handle the response
render(){
//Set the result of the API call to state.itemFromApiCall
return <div><Child item={this.state.itemFromApiCall} /></div>
}
}
And your Child component would look like this:
function Child (props){
if(!props.item)
return <span>Loading...</span>
//Then you have your component as you had before
}
On first Render, Child will not crash because it knows that prop.item might be undefined. Whenever you actually get the value of the API call, Child will behave as expected.
Hope it helps!

ComponentWillMount and ComponentDidMount not synced

I am making a react application. I need to make an ajax call to fetch the data from server and populate this data in the rows of my component's table. I do my ajax call in componentWillMount and store the response in the state. Now my render fetched this state and populates it. I have a few questions though:
Am I doing it right to place ajax in componentWillMount?
My componentDidMount doesn't get the value of state which get's set in componentWillMount. This means the async is not complete while componentDidMount is trying to access it. How to solve this? setting async:false is really a bad option.
What all thing can be used in componentWillMount?
Yes, componentWillMount is called once before the render. This is the place to fetch data.
Probably when the component finished rendering, the componentDidMount called although the async operation didn't finished yet, therefore you get empty result.
You can use componentWillUpdate
componentWillUpdate() is invoked immediately before rendering when new
props or state are being received. Use this as an opportunity to
perform preparation before an update occurs. This method is not called
for the initial render.
Or shouldComponentUpdate,
shouldComponentUpdate() is invoked before rendering when new props
or state are being received. Defaults to true. This method is not
called for the initial render or when forceUpdate() is used.
It's a broad question, basically fetching data from actions, calling api's etc.
More info in docs
Am I doing it right to place ajax in componentWillMount?
The recommend approach is to place your initial network requests that modify state inside of componentDidMount, because in theory your ajax request could come back prior to React completing the mounting process which might lead to your state getting overwritten.
However, in practise people use both as the above scenario should never happen.
My componentDidMount doesn't get the value of state which get's set in
componentWillMount. This means the async is not complete while
componentDidMount is trying to access it.
That is the correct behaviour.
How to solve this? setting
async:false is really a bad option.
If you stop it then it's no longer async.
The best practise here would be to show a loading indicator:
add a loading property to state
set it to true just above your fetch request
inside .then and .catch set loading to false
inside render check if your data has arrived - if not display the loading indicator
If you don't want to show anything until your request comes back
You can return null inside of render which will prevent your component from being added to the DOM. You can then combine this with a flag state property to make sure the component gets mounted once your request comes back, i.e. something like this:
render() {
if (!this.state.initialRequestHasCompleted) return (null);
return (
<div>
...
</div>
);
}
What all thing can be used in componentWillMount?
What is the purpose of having functions like componentWillMount in React.js?

Redux global solution for Cannot read property of undefined in mapStateToProps if store is not yet hydrated

After migrating to react redux land, I'm in love.. yet my heart breaks every time i try to render a route and gets a Cannot read property 'name' of undefined in the mapStateToProps function which is trying to extract a slice of state that is not loaded yet.
Example:
state = { posts:{ blog : {byId:{},ids:[]}, wall: {byId:{},ids{} } };
Say I do fetch all data from the server once the app starts up "index.js entry point". Based on the assumption that all data is loaded, I code multiple components to filter this data and work on it.
Trouble happens when a user opens the app and navigates too fast to a page where the data is not yet loaded. Then I get the hated error of "Cannot read property 'name' of undefined"
Example:
//route ::/posts/blog/1 has something like this
export default connect(
(state,prop)=>({
postId: state.posts.blog.byId[props.params.id].id
}))(props=><div>Post Id:{props.postId}</div>);
Now this components if loaded before blog store is populated it will give an error.
An option is to return a placeholder to connect and check first that post is loaded, or to use a magical proxy in initialState
initialState.blog = {byId:new Proxy({},{get:(obj,name)=>(name in obj)?obj[name]:{}}),ids[]}
//this will suppress error by returing an empty object if undefined byId
Is there any way i can REQUIRE a component to load its data by dispatching an action for example before it even renders?
What i want is something like what propTypes does except that it will defer loading the component or its connect function until the store confirms that it has finished loading.
Idea from propTypes:
Component.propTypes = {
dispatch:PropTypes.func.isRequired,
};
This will give an error if dispatch is not supplied as a prop for component
i want something similar but for store.
Example:
Component.requireData = (state)=>({
posts.blog:<placeHolder actionToDispatch="POST/BLOG/LOAD"/>, //if not posts.blog.isLoaded render placeholdercomponent insteed.
});
So it happens automagically if the required data is not loaded yet, the component will not render, and the placeholder will render in place dispatching an action that load the required missing data.
Whenever a component's render is called, it means the parent decided the component should exist. mapStateToProps is free to examine the state in any detail it needs to and produce any props from it as it sees fit. If a part of the state is missing, it can substitute some other props.
Some part of your system will have to decide when a place holder should be rendered rather than the data presentation component. The component that decides this may be the component itself or a parent. If it's a parent then you can make this component reusable.
No matter which way you turn this, you have to code the part that decides if all the data is ready. There's no magic. At the very least you'd need to pass a field name, but a function would be more flexible. At this point you may as well plug this function into the connect directly.
As such it seems to me that just writing a simple reusable parent component that takes a boolean prop that decides whether to show the child or a placeholder is not only a good and simple solution, but it seems to me that any other solution may not be substantially better or more "magical". Also "magic" is generally regarded as a bad idea these days because it implies a lack of transparency. By sticking to the React-Redux way, you are not adding hard to understand code.
Finally, don't expect either React or Redux to know what to do when your app doesn't provide the data that your app requires. Either don't require it or provide it. Creating a simple reusable parent component solves the issue by not requiring the data (and deciding what to do instead, which neither React, nor Redux, nor probably a third party library knows to decide).
Recap: create your own magic. It's simple, quick, and perfectly tuned to your needs.

Resources