Updating the state of an array on React - reactjs

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} />
})

Related

Components not rendered when receiving arrays from parents

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} />;
})

Adding to a state array without the resulting array being read only, in React Native?

Im programming a component where I am mapping over a component using an array I have stored in state.
const [animalList, setList] = useState(['cat', 'dog'])
{ animalList.map((tag) => {
return (
<AnimalButton animalz={tag}/>
)
})
}
I want to add to the state array to force to make the app rerender. I attempted to do so with a .push function, store the increased array in a temporary variable and assign that new array to the state, as push() and unsplice don't return the actual array.
onSubmit={(values, actions) => {
actions.resetForm();
animalList.push(values.pet)
let newList = animalList
setList(animalList = newList)
}}
However I get this error [TypeError: "animalList" is read-only]?
Is there a way to change add to animalList without my resulting list in the state becoming read only?
Yes you cannot push this into const.
However, you can use this approach.
setList([...animalList,values.pet])

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>)
}

Understanding React refs - why is my reference null here?

I have a use-case where a React ref makes sense.
I've tried a few different ways of implementing them, and in this case integrating them with hammerjs.
I'm mostly going off of this question:
adding hammerjs to a react js component properly
My return method in my render is as such:
return (
<div className={"App card-row card-color " + this.props.className} ref={
(el) => this._slider = el
}>
{this.state.bubblemsg ? (
<NotifBubble message={this.state.bubblemsg} merchant={this.props.merchant.merchant}/>
) : (
null
)}
<ScrollMenu
data={this.state.list}
inertiaScrolling={true}
transition={.1}
inertiaScrollingSlowdown={.000001}
/>
</div>
);
Which I would think would attach my div element as a reference.
In my componentDidMount() method, I am then attaching it to hammer:
componentDidMount() {
this.hammer = Hammer(this._slider)
this.hammer.on('swipeleft', console.log("swipe left"));
this.hammer.on('swiperight', console.log("swipe right"));
}
However, I am getting the error:
TypeError: Cannot read property 'addEventListener' of undefined
And this is directly related to Hammer, and thus the reference I assume.
So what am I doing wrong with my references? I don't totally understand how they're supposed to work and the React tutorial explanation wasn't super clear, so a thorough explanation would be useful.
I think the problem is that in the listening of the hammer you have to pass a function to be called, try inserting an arrow function to log
componentDidMount() {
this.hammer = Hammer(this._slider)
this.hammer.on('swipeleft', () => console.log("swipe left"));
this.hammer.on('swiperight', () => console.log("swipe right"));
}

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