React best way to handle empty object before mount? - javascript

I have a React class, and it wants to render an object shown below:
data: {
title: "haha",
description: {
country: "US",
year: "1996"
}
}
But, when React wants to render it, it gives an error.
Uncaught Error: Invariant Violation: receiveComponent(...): Can only update a mounted component
I think the problem is in the getInititalState, I declare my data as an empty object, so when I get my full data object after the timeout, React will try to map my data object to the component, but it gives error.
But one interesting thing is, I have no problem accesing this.props.title.title, but not this.props.title.description.country, it will give undefined
But, when I console.log it, I could see my object. But React cant access it!
My guessing is when React initializing from the empty object, it will only init the virtual DOM with the 1st and 2nd level of the data object.
That the reason, when I try to access this.props.data.data.title is OK but not this.props.data.data.description.country
Below is my code
var BookBox = React.createClass({
getInitialState: function() {
return { data: {} };
},
componentWillMount: function() {
var that = this;
setTimeout(function() {
console.log('timeout');
that.setState({
data: {
title: "haha",
description: {
country: "US",
year: "1996"
}
}
});
}, 2000);
},
render: function() {
return (
<div>
<h1>{this.state.data.title}</h1>
<TestBox title={this.state.data} />
</div>
);
}
});
var TestBox = React.createClass({
render: function() {
console.log(this.props.title);
return (
<div>
<p>{ this.props.title.description.country }</p>
<p>{ this.props.title.title }</p>
</div>
);
}
})
May I know what is the best way to handle this problem? should i init my data object structure in the getInitialState or there is a better way?

I think you are getting Can only update a mounted component error because you are using componentWillMount and settimeout together, but you dont know if the component has been mounted by the time settimeout function fires.
Since you know what your state is beforehand, I think it is best to return your data from getInitialState function.
You can also use componentDidMount instead of componentWillMount function. That way you can be sure the component is mounted when componentDidMount is called.
Any time time you are using asycn functions like settimeout or a xhr call, you should use this.isMounted() in the callback function, to check that the component is still mounted by the time the callback fires.
For example, if you didnt know the state beforehand, you could fire an xhr call in the componentDidMount function, check this.isMounted() in the success callback and setState.
As for the error on <p>{ this.props.title.description.country }</p> line: At initial render this.state.data (BookBox) is an empty object so is this.props.title(TestBox). Accessing empty object's ({ }) title property is undefined. No problem there. Accessing description is also undefined. But accessing undefined's country is error. To avoid this error you can create a description variable: description = this.props.title.description || {} and use <p>{description.country}</p> to make sure your code doesnt break if this.props.title is empty.

Related

BeforeMount not working on a computed method

Hello i have a problem whenever i reload my page i want to call a beforeMount() method to call my filterRecords (computed method) so that i can receive the data ,but it is just not working fine it is telling me that filterRecords is not a function.
My computed:
computed: {
...mapGetters({
sec: "sec"
}),
createdDate() {
return moment().format("DD-MM-YYYY ");
},
createdHours() {
return moment().format("HH:mm ");
},
filteredRecords() {
return this.records.filter(record => {
return record.template_id === this.sec;
});
}
},
so i just do this:
beforeMount() {
this.filteredRecords();
},
it is not calling it so i get nothing to my filteredRecords.
how am i able to call it when i reload the page ( i am recieving the records data from an API rails)
Be sure to check the Vue docs about computed properties.
You should not call them as functions, but as properties (since they are computed properties).
You can try to console log filteredRecords in the beforeMount hook like this:
beforeMount() {
console.log(this.filteredRecords)
},
This seems like a fundamental misunderstanding on how Computed properties work. Computed properties are accessed the same way you would access props or data on a component. They are consumed as values, not as methods. A big hint to this fact is that you're calling map**Getters**
You could consume filteredRecords in your template something like:
<div v-for="record of filteredRecords" :key="record.someKey">...</div>
or by assigning it to a data property
this.records = this.filteredRecords

How the props of react.js are declared

I'm new to React. I'm not understanding a part in this code:
var HelloMessage = React.createClass({
render: function() {
return <h2>{this.props.message}</h2>;
}
});
var TextBox = React.createClass({
getInitialState: function() {
return { isEditing: false }
},
update: function() {
// Where is props.update defined? //
this.props.update(this.refs.messageTextBox.value);
this.setState(
{
isEditing: false
});
},
edit: function() {
this.setState({ isEditing: true});
},
In the code I can't find the props declaration for update. But looking through the code we should see "update" as a property of the TextBox component.
I'm not seeing an explicit declaration of this.props.update within any render method.
How/Where is props.update defined?
So inside the HelloReact component render method, a few TextBox components are returned like so:
...
<TextBox label='First Name' update={this.update.bind(null, 'firstName')}>
...
Now here what is happening is that HelloReact is passing a prop named update to this TextBox component. This means that inside the TextBox component I will be able to use this prop with this.props.update. Every prop that is passed down from the parent will populate this.props of the child. In this specific case we are passing down label and update.
Now inside the TextBox component we will be able to access these props intuitively with this.props.label and this.props.update. Inside this component it's define a private method that is called update, this is the code from the snippet you posted with better formatting:
...
update: function() {
this.props.update(this.refs.messageTextBox.value);
this.setState({ isEditing: false });
},
...
So here we are calling this.props.update which is the prop that was passed down from the parent in HelloReact. The reason we are wrapping this call in a private method is because on top of being able to call this.props.update() we also want to do something else, in this case we want to update the state of the TextBox component as well.
I hope this explanation was clear enough. I suggest reading about React from the official docs which are pretty amazing, or watch any of the many tutorials online. These are key concepts of React and you need to understand them properly in order to be able to develop in React.
For this case you might wanna read this, which comes from the offical docs and is about props.

Use props instead of state

I know if you want to alter something within a component itself you use state, and when the emit is external then u use props to receive it. But today I stumped across this example
var Label = React.createClass({
handleClick: function(){
console.log("Click");
this.props.children = "Text After Click";
this.setState({liked: false});
},
render: function () {
console.log("Render");
return (
<p ref="p" onClick={this.handleClick}>{this.props.children}</p>
);
}
});
Used props instead of state to change the text a value of a button. I'm confused now. The link to the source is here http://myshareoftech.com/2013/12/unit-testing-react-dot-js-with-jasmine-and-karma.html
I don't know about the source but when i tried above code it throws this error:
Uncaught TypeError: Cannot assign to read only property 'children' of object #<Object>.
It should not work because the basic property of the props is, as per DOC:
Props are Read-Only, Whether you declare a component as a function or a class, it must never modify its own props. All React components must act like pure functions with respect to their props.
Check the fiddle for error: https://jsfiddle.net/pjnp6yza/
Reference: https://facebook.github.io/react/docs/components-and-props.html#props-are-read-only

Can't access object property on a React object state, even though it exists. Returns undefined

I'm using AJAX to get some JSON, and then I want to display the value. If I log out he object that contains the value I want to display, I can see the key and the value. However, when I try to access the value directly, I get undefined.
Here is the component that I am stuck on:
var WeatherCard = React.createClass({
getInitialState: function () {
return {};
},
componentDidMount: function() {
var comp = this;
$.get("http://api.openweathermap.org/data/2.5/weather?zip=" + this.props.zipcode + ",us", function(data) {
comp.setState(data);
});
},
render: function() {
// I want to get the value # this.state.main.temp
// this works...
console.log(this.state.main);
// this does not work...
// console.log(this.state.main.temp);
// I want "current temp" to display this.state.main.temp
return (
<div className="well">
<h3>{this.state.name}</h3>
<p>Current Temp: {this.state.main} </p>
<p>Zipcode: {this.props.zipcode}</p>
</div>
);
}
});
Here is the whole plunk.
http://plnkr.co/edit/oo0RgYOzvitDiEw5UuS8?p=info
On first pass, this.state is empty which will render this.state.main.temp as undefined. Either you prefill the state with a correct structured object or wrap in if clauses.
For objects that returns null or undefined React will simply skip rendering it without any warnings or errors, however when you have nested structures that return null or undefined normal JavaScript behaviour is employed.
Try setting your initial state using main: [], data: [] rather than an empty object.
I was having the same problems and once I took my AJAX setState and made an empty initial state everything started working fine.
I'm new to React so I can't give you a very good explanation as to why this made a difference. I seem to stumble into all kinds of strange problems.
Hope this helped.

React js: Can´t send first object in array as prop

Im trying to build an small React.js application and my component structure looks like this:
MainComponent
- CategoryList
-Category
- ItemsList
-Item
My MainContent component does an ajax request for its state data in the componentDidRender: which returns this object:
data:[
Object[0]
-name
-items[]
,
Object[1],
Object[2]
]
Now, I want my CategoryList to write out all the Categories by name, which works just fine, but I also want to print out the items of the selected category. This is my ItemsList component:
var ItemsList = React.createClass({
render:function(){
var itemNodes = this.props.category.items.map(function(item){
return (
<Item name={item.name} />
);
});
return(
<div className="itemList">
{itemNodes}
</div>
);
}
});
And this is how I pass on the "category"-property from my the parent component
<ItemsList category={this.state.data[0]} />
I get an error say "Can´t read property items of undefined" meaning that the category prop never was assigned. I know that this.state.data contains an array of objects so I don´t see the error here.
What do I do wrong?
EDIT: Per request, this is my MainComponent:
var MainComponent = React.createClass({
getInitialState:function(){
return {data: []};
},
componentDidMount:function(){
$.ajax({
type:'get',
url: '/categories',
dataType: 'json',
success:function(data){
this.setState({data: data});
}.bind(this)
});
},
render: function(){
return (
<div className="row">
<div className="col-md-6">
<CategoryList categories={this.state.data} />
</div>
<div className="col-md-6">
<ItemsList category={this.state.data[0]} />
</div>
</div>
);
}
});
Your main component initializes the state with an empty array in data. A render would always fail because there is no this.state.data[0].
One would probably reply that the ajax request will provide the value for this state property data (supposing that your web service is providing a valid array). However, this only happens after the response was received from the server, which will not happen after the first render.
If the information was available immediately, one could either setState on the method componentWillMount or the component constructor, so as to avoid triggering a second render:
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state synchronously in
this method will not trigger a re-rendering. Avoid introducing any
side-effects or subscriptions in this method.
In this case, since we are waiting for remote information, the React documentation still recommends the use of componentDidMount, as well employed here:
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here. If you
need to load data from a remote endpoint, this is a good place to
instantiate the network request. Setting state in this method will
trigger a re-rendering.
Therefore, the component's render method must be able to handle the missing state variable. There are multiple ways to approach this, but preventing the nested element from being rendered until we have data is the easiest approach. With some additional logic, the application could inform the user that the particular component is loading.
render() {
return (
<div className="row">
<div className="col-md-6">
<CategoryList categories={this.state.data} />
</div>
<div className="col-md-6">
{this.state.data.length > 0 &&
<ItemsList category={this.state.data[0]} />
}
</div>
</div>
);
}
I can corroborate the previous answer.
Modern (2023) React tells you to use functional components. So, if you want to upload data from a server to render it inside a component you need to use so-called 'hooks' named 'useEffect' and 'useState'.
So first you import them:
import React, { useEffect } from "react";
import { useState } from "react";
Next, you create a functional component and initialize 'state' and 'set_state function' inside a component:
export default function FuctionName() {
let [ value, set_value ] = useState([])
}
This would create the 'value' variable which would preserve its state across renders.
Then, you make a fetch request (before the component's return statement) (but you need to put it inside of the useEffect function for a consistent behavior):
useEffect(() => {
fetch('url')
.then(response => response.json())
.then(server_data => {
set_value(server_data);
})}
, [])
Now, you see why we need state: because we need to store data returned from the server inside a variable and we need this data to preserve across renders.
Also, you think that now you can use your data inside the component's return render like this:
return (
<h1>the data from server: {value.name_of_key}</h1>
)
But you encounter the error of 'reading undefined'.
First, check if the server returns the correct type of object. Put a console.log statement inside the fetch request:
useEffect(() => {
fetch('url')
.then(response => response.json())
.then(server_data => {
set_value(server_data);
console.log(server_data);
})}
, [])
It should be a list with objects inside, like: [{}, {}, {}].
If it is, then the problem is that component renders before it receives the data from the server. The component will first render, then receive the data from the server. If you call the state variable inside the render statement it will call the empty state.
So, in order to mitigate it, your code should be ready to render both the empty state and the state with data. The easy way to do so would be conditional rendering. Render the component with data only if there is data to render:
return (
{value && <h1>the data from server: {value.name_of_key}</h1>}
)
the 'value &&' part lets you implement the logic:
if the value (data from the server) is empty don't render anything (thus do not call any keys from data)
if the value is not empty, render the component which calls to data
Why is that so? The '&&' expression (a and b) evaluates from right to left. If first variable evaluates to 'false', then it does not go further and stops there. The initial state of variable 'value' is an empty object ('let [ value, set_value ] = useState([])' - remember?). So, when the server has not returned data yet on the first render, the expression 'value &&' evaluates to 'false' and does not render the right part of the expression. When the server has returned the data, it calls the 'setState' function with data and forces to render the component the second time. The second time expression 'value &&' evaluates to 'true' and React goes to the right part of the expression and actually renders the component with data. You can check this process by placing console.log statement inside the fetch request and inside the return statement and you will see the order of functions resolutions.
Hope it helps.
Also, check out the article:
https://daveceddia.com/react-before-render/
Happy coding >>

Categories