Passing props and mapping data in React - javascript

I'm working on a project that requires me to pass data to two functional components.
My axios call to the API seems to work, as well as setting the state with hooks, but I keep receiving these two errors:
Error Cannot convert undefined or null to object I tried checking if the array is empty but that didn't seem to solve the problem
summaryMetrics is not defined - This I don't understand because summartMetrics is defined. Could it be that the elements are being displayed before the data is fetched ?
Here are the files in a codesandbox
Included the API's so people can see the structure of how the JSON is being returned.
Im not sure if I'm passing and mapping the data correctly.
Here are the files in a codesandbox
Any help is very much appreciated

Since your state has this form :
const [summary, setSummaryData] = useState({
summaryMetrics: null,
platformsData: null
});
...you should access your state like this :
<SummaryMetrics
uniqueSocialMediaPost={summary.summaryMetrics[0]["uniqueSocialMediaPost"]}
positiveScore={summary.summaryMetrics[0]["positiveScore"]}
riskScore={summary.summaryMetrics[0]["riskScore"]}
/>
[...]
Note the "summary." before "summaryMetrics" :
summary.summaryMetrics[0]["uniqueSocialMediaPost"]
Fixed Fixed Code Sandbox (other things seems to go wrong though, white screen, but syntax error is fixed) :
Previous errors were :
mocky.io serves content over HTTP, Code Sandbox over HTTPS so mixed content issue : Chrome does not allow this kind of mix in protocols, fixed by using HTTPS protocol to grab infos from mocky.io,
no unique keys for SummaryByPlatform component in map function
Chrome dev tools is your friend when nothing happens as expected :).
By the way you could just
summary.summaryMetrics.map(s => <SummaryByPlatform summaryMetrics={s} />)
...instead of gettings keys, and you could simply pass whole summaryMetrics object to SummaryByPlatform and have only one props. But that's another subject.
Good luck with it.

Your issue is that you're not waiting for summaryMetrics to load. It's trying to read the array before it's been fetched.
What I would do, is put the render inside a function and conditionally load it once the data is available.
So your Summary.js file would look like this:
const renderSummaryMetrics = () =>
summary &&
summary.summaryMetrics && (
<div>
<SummaryMetrics
uniqueSocialMediaPost={summary.summaryMetrics[0]["uniqueSocialMediaPost"]}
positiveScore={summary.summaryMetrics[0]["positiveScore"]}
riskScore={summary.summaryMetrics[0]["riskScore"]}
/>
{Object.keys(summary.summaryMetrics) &&
Object.keys(summary.summaryMetrics).length > 0
? Object.keys(summary.summaryMetrics).map(keyName => (
<SummaryByPlatform
platform={keyName}
mean_sentiment={summary.summaryMetrics[keyName].mean_sentiment}
post_count={summary.summaryMetrics[keyName].post_count}
positive_posts={summary.summaryMetrics[keyName].positive_posts}
negative_posts={summary.summaryMetrics[keyName].negative_posts}
/>
))
: null}
</div>);
return renderSummaryMetrics();

There are several problems:
summaryMetrics is indeed not defined. It was actually defined as summary.summaryMetrics.
summary.summaryMetrics[0][...] will cause another "undefined error", because it was defined with default value of null.
Explanation for Problem 1:
No further explanation needed.
Explanation for Problem 2:
useEffect in React functional component will only be run after the component is being rendered.
This means that when you execute summary.summaryMetrics[0][...], summary.summaryMetrics is actually null because you defined it as the default value. Therefore, the steps behind will generate another error because null[0] is not possible.
What you need to do is to check whether each object properties down the tree is actually a valid object before you call the property. Otherwise, the error will happen down the property tree.
I can't show you how to correct your code, because I would need to change a big part of you code. What you can try is to mainly check the presence of values of summary.summaryMetrics first, before calling it's children properties.

Related

Do React console warnings need to be dealt with before deploying?

I've been learning to code for about 3 months now and I've gotten pretty far with an APP I created but when I inspect the page, I have over 50 warnings. Here are some of the warnings
react-dom.development.js:67 Warning: Text content did not match. Server: "Sign out" Client: "Create"
at a
at O (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1647879270456:148896:19599)
at Link (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1647879270456:89219:19)
THERE ARE BOUT 10 LINKS SIMILAR TO THE ONES ABOVE THAT FOLLOW
VM2101 react_devtools_backend.js:3973 Warning: Each child in a list
should have a unique "key" prop.
Check the render method of AllAssets. See
https://reactjs.org/link/warning-keys for more information.
at O (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1647879270456:148896:19599)
at AllAssets (http://localhost:3000/_next/static/chunks/pages/index.js?ts=1647879270456:40:73)
THERE ARE BOUT 10 LINKS SIMILAR TO THE ONES ABOVE THAT FOLLOW
VM2101 react_devtools_backend.js:3973 Warning: The tag is
unrecognized in this browser. If you meant to render a React
component, start its name with an uppercase letter.
at image
at O (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1647879270456:148896:19599)
at span
at O (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1647879270456:148896:19599)
THERE ARE BOUT 10 LINKS SIMILAR TO THE ONES ABOVE THAT FOLLOW
These equal out to about 100 lines of errors all seeming to be referring to the same issues. To be honest, I don't really know how to address tese. Like for example, the one that sasys that all child lists should have a key prop. I'm pretty sure it's referring to some instances where I have the following:
return (
<ul>
{assets.map((result) => {
const {
collection: { name },
data: { image },
} = result;
if (name !== null) {
return (
<Container>
<Card key={name}>
<CollectionAvatar
image={image}
/*name={name || collection}*/
type="collection"
/>
<br></br>
<br></br>
{name}
</Card>
</Container>
);
}
})}
</ul>
);
}
You see, I added a key, but I don't really know if that's the right way to do it or if I added the wrong key etc. I'm still learning about this.
Typing this up I realize that I have a few questions:
Are the errors that I have shown critical? Do they need to be fixed before deploying?
Is there anyway to hide these in the meantime if they are not critical so that I can fix them at a later time?
How can I find out what the error is talking about. I see a link but I don't understand what the result is showing me.
Thanks for you patience. Like I said, I'm learning and I realize I still have a lot to learn but I'd appreciate some clarity on this!
Well, you could deploy your app with warnings. But you should keep in mind that they could lead to unpredictable behaviors in the future, so address them as soon as possible. If I remember correctly, they will still show up the console, but you will be able to deploy the app. It's also worth remembering that some CI servers (like netlify) don't allow you to deploy your app with warnings, so you need to set an environment variable before deploying. (CI=false)
It's hard to know exactly what is the problem in your code just by looking at the logs. But to solve the one related to the keys, you need to pass a unique key as props to the component that is being returned by the map function. So in your case, instead of passing key={name} to the Card component, try passing it to the Container component.

Expected server HTML to contain a matching <tag> in <tag>

We are using nextjs and getting this error at page refresh (or first load)
My error is:
react-dom.development.js:88 Warning: Expected server HTML to contain a matching <tag> in <tag>.
The code of our functional component looks like this:
export default MyComponent () {
if(! props.something){ // ← this is causing the problem.
return null;
}
return (
<>
HTML here ...
</>
)
}
From my understanding, SSR is different from client side rendering and this is why react is complaining.
The app is working fine but this error is showing in the console and we don't want to have many errors being thrown there as this may prevent us from seeing the real errors when they happens.
Solution:
The solution is to use dynamic imports and and wrap the component call into:
const MyDynamicComponent = dynamic(() => import('./myComponent'), {ssr: false});
//use it:
<MyDynamicComponent />
//OR :
const MyDynamicComponent = dynamic(() => import('./myComponent'))
//use it:
{typeof window !== 'undefined' && (
<MyDynamicComponent />
)}
May be importing your component dynamically should solve this.
Below is the link you can refer;
https://nextjs.org/docs/advanced-features/dynamic-import
I've been looking for solution of a similar problem, but I was using react-window lib and the problem appeared to be in it. I passed different height to it, depends on whether it's loading on server or client, so it gave me back different elements.
My solution was to pass the same height at the beginning, but to change it to window.innerHeight on useEffect ( = on client load).
When this happened to me on a Next project, I had not nested my html table elements properly. Specifically, didn't wrap my <tr> in a <tbody>
the code is executed first on the server after in the browser in my case this is why i get this error and other error with style component like :
Warning: Prop className did not match. Server: "CardUserInfo....
so this link help :
https://github.com/vercel/next.js/discussions/17443
enter link description here
and the solution for me like montienned in this disscussion implement useState useEffect
const [mount, setMount] = useState(false)
useEffect(() => {
setMount(true)
}, [ ])
return mount&&(
<InfoUserCard/>)
In my case the console error displaying Warning: Expected server HTML to contain a matching <sup> in <span>. using Safari Web Browser in development ONLY, i.e. localhost:3000 . Just get rid of this boring error by using Chrome Web Browser.
This boring error will not be exist in production with Safari Web Browser.

Error when trying to access an object inside an array with React

I'm trying to access a property on an array that has on index 0 an object and I'm trying the following: object[0].main But the console throws me an error.
I'm using React on codepen so it doesn't show me the error pretty well, I want to access that property to be able to put it on another object that I'll pass down as props (because is something I hold on my state). Here is my code: https://codepen.io/manAbl/pen/aGymRg?editors=0011 Look for line 56.
This is stressing me out.
Thanks in advance
This happens because the data is not loaded from the server yet so the object will be initially as you set it to {}
so to fix this you can do
instead of
description: this.state.weather.weather[0].main,
do
description: this.state.weather.weather && this.state.weather.weather[0].main,
The idea is you set the initial state to : {} correct? and render is called before the ajax is actually finished since its async.
so on first render it will be "{}" and you are doing [0].main on it. which will crash.

Parsing JSON objects array and displaying it in ReactJS

I've been facing a weird issue lately with my React App. I'm trying to parse a JSON object that contains arrays with data. The data is something like this:
{"Place":"San Francisco","Country":"USA", "Author":{"Name":"xyz", "Title":"View from the stars"}, "Year":"2018", "Places":[{"Price":"Free", "Address":"sfo"},{"Price":"$10","Address":"museum"}] }
The data contains multiple arrays like the Author example I've just shown. I have a function that fetches this data from a URL. I'm calling that function in componentDidMount. The function takes the data i.e responseJson and then stores it in an empty array that I've set called result using setState. In my state I have result as result:[]. My code for this would look something like this:
this.setState({result:responseJson})
Now, when I've been trying to access say Author Name from result I get an error. So something like this:
{this.state.result.Author.Name}
I'm doing this in a function that I'm using to display stuff. I'm calling this function in my return of my render function. I get an error stating :
TypeError:Cannot read property 'Name' of undefined. I get the same error if I try for anything that goes a level below inside. If I display {this.state.result.Place} or {this.state.result.Country} it's all good. But if I try,say {this.state.result.Author.Title} or {this.state.result.Places[0].Price} it gives me the same error.
Surprising thing is I've parsed this same object in a different component of mine and got no errors there. Could anyone please explain me why this is happening?
If I store the individual element while I setState in my fetch call function, I can display it. For example:
{result:responseJson,
AuthorName:responseJson.Author.Name
}
Then I'm able to go ahead and use it as {this.state.AuthorName}.
Please help me find a solution to this problem. Thanks in advance!
It could be that your state object is empty on the first render, and only updated with the data from the API after the request has completed (i.e. after the first render). The Name and Place properties don't throw an error, as they probably resolve to undefined.
Try putting an if block in your render method to check if the results have been loaded, and display a loading indicator if they haven't.
I'm guessing your initial state is something like this:
{ results: {} }
It's difficult to say without seeing more code.
[EDIT]: adding notes from chat
Data isn't available on first render. The sequence of events rendering this component looks something like this:
Instantiate component, the initial state is set to { results: [] }
Component is mounted, API call is triggered (note, this asynchronous, and doesn't return data yet)
Render method is called for the 1st time. This happens BEFORE the data is returned from the API request, so the state object is still {results: [] }. Any attempts to get authors at this point will throw an error as results.Authors is undefined
API request returns data, setState call updates state to { results: { name: 'test', author: [...] } }. This will trigger a re-render of the component
Render method is called for the 2nd time. Only at this point do you have data in the state object.
If this state evolves, means it is changed at componentDidMount, or after a fetch or whatever, chances are that your state is first empty, then it fills with your data.
So the reason you are getting this error, is simply that react tries to get this.state.result.Author.Name before this.state.result.Author even exists.
To get it, first test this.state.result.Author, and if indeed there's something there, then get Author.Name like this.
render(){
return(
<div>
{this.state.result.Author ? this.state.result.Author.Name : 'not ready yet'}
</div>
);
}
[EDIT] I'll answer the comment here:
It's just because they are at a higher level in the object.
this.state.result will always return something, even false if there is no result key in your state (no result key in your constructor for instance when the component mounts).
this.state.result.Country will show the same error if result is not a key of your state in your constructor. However, if result is defined in your constructor, then it will be false at first, then become the data when the new state populates.
this.state.result.Author.Name is again one level deeper...
So to avoid it, you would have to define your whole "schema" in the constructor (bad practice in my opinion). This below would throw no error when getting this.state.result.Author.Name if I'm not mistaken. It would first return false, then the value when available.
constructor(props){
super(props);
this.state={
result: {
Author: {}
}
}
}

How can I make my React child components render even if a prop is missing

TLDR; I need to be able to render child components in React even if a property of this.props is missing.
I have an React app built with Yahoo's Flxubile. The app fetches data from a Wordpress site with WP REST API. Sometimes an image might be missing or something else from the API, this causes the client to break. Here's an example:
I have a file called Articles.js which is connected to ArticlesStore which holds my articles. I then render one Article.js for every article I have and pass props like this:
{ this.props.articles.map(function(el, index) {
return <Article key={index} article={el} />
})
}
Everything is ok here, but then in my Article.js when I try to access properties that is not set I get the following:
Uncaught TypeError: Cannot read property 'sizes' of undefined
This is the line which causes the error:
<img src={this.props.article.meta_fields.image.sizes.large} />
This happens when a image is missing from an article. I understand the javascript error of course but I want to render the Article.js component event if a image url is missing from the API/Store. I have tried the following solutions, but it causes too much clutter and no control:
Conditional set if we have the prop i.e.
{this.props.article.meta_fields.image ? this.props.article.meta_fields.image.sizes.large : "imagemissing.png"}
Setting defaultProps. This does not work because passed properties from parent to child overwrites defaultProps.
Maybe I should try something else than passing props from parent to child? Have a ArticleStore where I can set default values for every article? How would you do it?
If you want to provide a nested structure as a prop (as with article) you'll want to be able to rely on the structure always being pretty much the same. In this case it won't be, sometimes the meta_fields does not have an image-attribute (as your TypeError suggests).
In your case I would consider pulling out the things you actually need/use in the Article component from the article object and pass those as props.
Say that your Article only uses title, body, and image. Then just pass those as props.
<Article title={ article.title } body={ article.body } image={ getImage(article) }/>
function getImage(article) {
if (article.meta_fields.image
&& article.meta_fields.image.sizes
&& article.meta_fields.image.sizes.large
) {
return article.meta_fields.image.sizes.large;
}
return 'default_image.jpg'; // Or whatever you want.
}
One might think that the extra props constitutes more "clutter" here, but given the choice between clutter and TypeError, I choose clutter.
And if you don't want to reinvent the wheel. The problem of accessing data in a nested structure like this have been solved before.
// Lodash
_.get(article, 'meta_fields.image.sizes.large', 'default_image.jpg')
// Ramda
_.pathOr('default_image.jpg', ['meta_fields', 'image', 'sizes', 'large'], article)

Categories

Resources