Components not rendered when receiving arrays from parents - reactjs

data.Example.map((item) => {
console.log(item.A)
return <ExampleBlock
a={item.A}
b={item.B}
/>
})
The data object in this code is imported from local json object. Here, console.log(item.A) just helps me to check if this is really an array.
postList.forEach(post => {
post.data.Example.map((item) => {
console.log(item.A)
return <ExampleBlock
a={item.A}
b={item.B}
/>
})
})
The postList here is imported from firestore. I exercise cautious here with console.log(item.A) and have verified that it's indeed an array like I drawn from local json file in the previous code set.
Both console.log shows the same thing.
Weird thing here is. The first set of code works just fine. It passed down the arrays as props and component is rendered perfect. The second set of code, the component wasn't rendered and it's just blank.
I've tried hard to think what's wrong and what's the difference. Would you guys please point me to the right direction. THanks.

It's due to the forEach which returns nothing. Replace it with a map as follows:
postList
.map((post) => post.data.Example)
.map((item) => {
console.log(item.A);
return <ExampleBlock a={item.A} b={item.B} />;
})

Related

Accessing State that holds Array of Objects

I apologize if this is a basic question, but I'm genuinely confused why this isn't working. I have a component that makes an API call to fetch data, and the data returns successfully, and I can do a console.log and see an array of objects as I expect.
.then((result) => {
this.setState({
surveyData: result,
surveyYear: result[0].year
});
console.log(result); <--- This logs an array of objects as expected
console.log(this.state.surveyData); <-- This logs an empty array
console.log(this.state.surveyYear); <-- This logs what I expect
})
When I use the return component, I get what I expect from this:
render(){
return(
<p>{this.state.surveyYear}</p> <--- this shows exactly what I'd expect
)
}
But if I do the below, it should show the exact same data, but instead, I get this error: TypeError: Cannot read properties of undefined (reading '0') Is it possible to do this way?
render(){
return(
<p>{this.state.surveyData[0].year</p> <--- I want to access the data this way instead
)
}
This is a common pitfall in react. The issue is that updating the state is not instantaneous. This is why your data is not there yet right after the call to setState. Similarly in the render method you will need to guard against that data not being available yet.
If you babel your code and support the optional chaining operator:
render(){
return(<p>{this.state.surveyData?.[0]?.year</p>)
}
otherwise
render(){
return(<p>{this.state.surveyData && this.state.surveyData[0] && this.state.surveyData[0].year</p>)
}

Changing an input parameter within React component

I am going through the code of a project and I see the following code:
export const FileLink = React.memo(({ url, data, ext, linkContent }) => {
...
...
if (!url.includes('?')) {
url += '?'
}
if (!url.endsWith('?')) {
url += '&'
}
return <a href={`${url}file_format=${ext}`}>{linkContent}</a>
})
It is working fine and no bugs appear in app behavior. But url is a passed parameter and it is changed within the FileLink: from what I read React components should be pure functions. So, I wonder whether its ok to do that, under which circumstances, and if not - why? What can go wrong? Any examples of how it could mess up the app?
(If interested to see the full code: https://github.com/broadinstitute/seqr/blob/8b4419285dfac9365c5c500bbb87b89808c0cedd/ui/shared/components/buttons/ExportTableButton.jsx#L37)
url is a local variable. Reassigning that variable, which is all this code is doing to it, has no possibility of affecting code outside of this function call. It doesn't make the function impure.
Now, if you were passed in an object, and you started mutating that object, then that would break purity. Because if the component that passed you this object is still using it, then it can "see" that change. For example:
const Example = ({ someObjectProp }) => {
someObjectProp.name = 'bob';
}

ReactJs : How to print data from console to web-page?

After a successful POST and GET query, the results of my operation are visible in the console of my React Dev Tools. How should I take those results, preferable create a table and render that table on my web-app itself?
Here is the GET request :
axios.get('http://localhost:5000/result')
.then((response) => {
console.log(response);
});
}
The format of results displayed on console is like this :
Let's say I want to create a table by traversing through the results with headers _id and Name. I know I should use the map function. But exactly where and how should I do that in my code?
You have a few options. You can make your call in componentDidMount, set the result in a state and then render that state.
componentDidMount() {
axios.get('http://localhost:5000/result')
.then((response) => {
this.setState({
data: response // maninpulate your response here
})
});
}
}
render() {
const { data } = this.state;
return this.renderTable(data) // this should return a table
}
Assuming you know the concept of 'useState' in react hooks. If not please have an understanding of it.
Long story short, useState is used to store the data in variables.
const [data, setData] = useState();
Instead of console.log(response); you set the response to the variable i.e; setData(response);
In html,
<table>
//some headers, if needed
//This will iterate your array of objects
{data.map((eachData) => (
<tr> <td>{eachData.id}</td>
<td>{eachData.name}</td>
....
</tr>
)
</table>
Please Note: The HTML part works for both class-based and function-based React components where as 'useState' is part of React hooks functionality, works only for function-based components.
I have created a small sample app where I have used react-table for displaying the data as a table. Also In this example I have added promise in order to simulate server fetching data from server. This you can replace with actual server call i.e., axis.get etc.
React-table provides a lot many features that might be helpful. Such as you can provide which columns you wish to display out of all the columns from the data.
If you do not wish to use any library for displaying table, that is also possible only that you have to replace ReactTable with your custom table implementation.
Hope this helps.
Thanks to this page: https://www.skptricks.com/2019/02/can-you-consolelog-in-jsx.html.
You can do console.log in the render function of a class component or in the return statement of a function component. In your case:
function Foo(props){
data = axios.get('http://localhost:5000/result')
return(
<>
{console.log(data)}
</>)
}
In my opinion, it's much more straight forward than the other state methods.

Updating the state of an array on React

I am having problems with the response data I get from an API. I use axios to get the JSON object, and the state should be replaced by the list of objects every time the API is called.
Here is the code.
https://i.stack.imgur.com/yHXn0.png
And here is the console
https://i.stack.imgur.com/KnHD2.png
I imagine I am updating the state wrong, but I cant see what it is.
Your error message is pretty clear.
Cannot read property 'push' of undefined
What does that mean? it means you called the method push on a variable that is still undefined
let paineis = empresas.map( (empresa, i) => {
paineis.push(<Painel key={i} nome={empresa.nome} usuarios={empresa.usuarios} />)
// ^-----^
// this is the issue. paineis has not yet been assigned a value!
})
The real issue here is you are using map incorrectly. the return value in a map is the way to push a new value. You want to do this instead
let paineis = empresas.map( (empresa, i) => <Painel key={i} nome={empresa.nome} usuarios={empresa.usuarios} />)
this is an ES6 shorthand for a return, also could be written
let paineis = empresas.map( (empresa, i) => {
return <Painel key={i} nome={empresa.nome} usuarios={empresa.usuarios} />
})

React + Firebase - Deleting firebase object inside mapped components

I'm trying to pass the key of my project object through to my mapped components in order to delete them.
UPDATE: using the _.forEach(project, (proj, key) => <Project key={key} {... more props here}/>) Throws an error about objects not being valid react children. I'm guessing this is because the project i'm forEaching over needs to be turned into an array? I keep trying to format in componentWillMount() but when i try to run forEach with setState and push to a new array i keep getting duplicates
componentDidMount() {
projectRef.on('value', snap => {
this.setState({projects: snap.val()})
// somehow i need to create a new array of objects that include the key.
})
}
UPDATE: i removed the codepen example. I like code sandbox better. Much better. =)
And here's the code sandbox (If you get an error about the [DEFAULT] app already being defined just refresh the output browser and it will work. I don't know why it's doing that... oh well. I added my attempt with forEach on the code sandbox example. Hopefully someone can let me know what i'm doing wrong.
Yep, map returns an array of the values of the object, in this case an object you can then access via the props in the <Display /> component, but not the key of each element of the object.
Perhaps you could use lodash's forEach in order to loop and have access to both the key and the value of each element in your data collection. Like that you can pass the key (that will be the target for the remove method) as a specific prop and the value as the item prop in the component.
export default class extends React.Component {
constructor() {
super()
this.state = {
items: null
}
}
render() {
return() {
<div>
{_.forEach(this.state.items, (item, key) =>
<Display key={key} itemKey={key} item={item}/>
)}
</div>
}
}
}
// then the display component
removeItem() {
firebase.database().ref({this.props.itemKey}).remove();
}
render() {
return (
<div>{this.props.item.name} <button onClick={this.removeItem}>X</button>
)
}
Here's a simple live example of how forEach works:
https://jsbin.com/xolejoj/edit?js,console
Edit 08-12-2017
The problem with your code is that you're missing the fact that map is returning the key and the value of each element of the object. In this case your object has a key string and a value that is the object. Then on your JSX you're trying to pass the key as it were a part of the object (value) but is not, therefore you're getting an undefined value in the component's props.
Change your code to this:
<div>
{_.map(projects, (proj, key) => <Project
key={key}
title={proj.title}
subtitle={proj.subtitle}
desc={proj.desc}
itemKey={key} // just the key of the object
/>
)}
</div>
The thing is that the key of each object is the identifier in firebase and the value is the object with the data you need, but that object doesn't have a key property, therefore it was evaluated to null.

Resources