State data is being duplicated after using setState inside of componentDidMount - javascript

I'm making a get() request to firestore to get data. I am able to store the data in my component state but each time I try to access the data, it shows as duplicated. How can I prevent it? Thanks.
``
import React from 'react';
import fire from '../../config/firebase';
class ViewLists extends React.Component {
constructor(props){
super(props)
this.state = {
listData: []
};
}
componentDidMount(){
fire.firestore().collection('restaurantList')
.get()
.then(querySnapshot => {
const lists = querySnapshot.docs.map(doc => doc.data());
this.setState({ listData: lists });
});
}
render() {
const { listData } = this.state
return (
<div>
{listData.map(list => console.log(list.user))}
</div>
);
}
}
export default ViewLists;
``

Related

Error: Cannot read property 'filter' of undefined, cannot get props

i am getting error while trying to filter out items in array. I suppose it is something connected with not right way to approach state or maybe i am not getting props. Main purpose is to filter trainings based on search value and show them in list.
Source code:
import React from 'react';
import axios from 'axios';
import Trainings from '../components/Training';
import CustomForm from '../components/Form';
class TrainingList extends React.Component {
constructor() {
super();
this.state = {
search: ''
};
}
updateSearch(event) {
this.setState({search: event.target.value.substr(0, 20)})
}
state = {
trainings: []
}
componentDidMount() {
axios.get('http://127.0.0.1:8000/api/')
.then(res => {
this.setState({
trainings: res.data
});
console.log(res.data);
})
}
render() {
let filteredTrainings = this.state.trainings.filter(
(trainings) => {
return trainings.title.indexOf(this.state.
search) !== -1;
}
);
return (
<div>
<Trainings data={filteredTrainings} />
<input type="text"
value={this.state.search}
onChange={this.updateSearch.bind(this)}/>
</div>
)
}
}
export default TrainingList;
You write:
state = {
trainings: []
}
to initialize trainings (You can remove this code). But the data your filtering is this.state.trainings.
So add the initialization of trainings to this.state:
constructor() {
super();
this.state = {
search: "",
trainings: []
};
}

Issue Passing Props After Fetch Request To Child Component (React)

I’m having trouble passing the state data from a parent component to a child component. I’m not sure why this is happening, so any feedback is greatly appreciated.
My fetch request is returning the correct data when I console.log ‘this.state.episodeData’ in the componentDidMount() method in the parent component, but it is not showing in the console.log in the componentDidMount() method in the child component. What am I doing wrong?
I’ve simplified the example to show only the relevant fetch request and data handling:
Parent component
import React, { Component, useState, Fragment } from 'react';
import TempComp from './tempComp';
export default class PostContent extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
episodeData: [],
}
}
async componentDidMount() {
const { id } = this.props.match.params;
const response = await fetch(`http://localhost:5000/episode/${id}/playlist`);
const jsonData = await response.json();
this.setState({
episodeData: jsonData, //this is working!
id: id
});
console.log('parent fetched data', this.state.episodeData)
}
render() {
return (
<Fragment>
<TempComp playlist={this.state.episodeData} />
</Fragment>
)
}
}
Child component
import React, { Component } from 'react'
class TempComp extends Component {
constructor(props) {
super(props);
this.state = {
}
}
componentDidMount() {
console.log(‘props in child component’, this.props.playlist)
}
render() {
return (
<div>
</div>
)
}
}
export default TempComp
componentDidMount is called just the first time after the initial rendering of your component. This means that your child component is rendered before you finish your fetch request. In the parent component you are not seeing it, because setState works asynchronously and it is not done saving your state, when you try to print it out. If you want to see it, pass callback to setState:
async componentDidMount() {
const { id } = this.props.match.params;
const response = await fetch(`http://localhost:5000/episode/${id}/playlist`);
const jsonData = await response.json();
this.setState({
episodeData: jsonData, //this is working!
id: id
}, () => {
console.log('parent fetched data', this.state.episodeData)
);
}
In order to see the updated data in your child component, consider using componentDidUpdate(prevProps, prevState, snapshot):
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('props in child component', this.props.playlist) ;
}

passing data from the parent component state to the child component using React.createContext

I have a component that contains a state, and I will pass the state data into another component, I use a static contextType to throw the state data but the data does not reach the intended component, what do you think this is wrong? thank you
this is my parent component
export const MyContext = React.createContext();
export class MerchantByPromo extends Component {
constructor(props) {
super(props);
this.state = {
dataPromo: [],
loading: true
};
}
async componentDidMount() {
const merchant_id = this.props.match.params.id_merchant
await Api.post('language/promo-voucher-by-merchant', { MERCHANT_ID: merchant_id })
.then((response) => {
if (response.data.STATUS_CODE === '200') {
this.setState({
dataPromo: response.data.DATA,
loading: false
});
}
})
}
this is my child component
import React, { Component } from 'react'
import { MyContext } from './MerchantByPromo'
export class MerchantByPromoDetail extends Component {
constructor(props){
super(props)
this.state = {
detailPromo:[],
}
}
UNSAFE_componentWillMount(){
let value = this.context
console.log(value)
}
componentDidMount(){
}
render() {
return (
<MyContext.Consumer>
<p>tes</p>
</MyContext.Consumer>
)
}
}
I always get an error message like this "TypeError: render is not a function", what's the solution?
<MyContext.Consumer>
{() => <p>tes</p>}
</MyContext.Consumer>
change to this and Check

react map is not a function

Yo guys, getting error 'contacts.map is not a function' not sure why is that ? just starting in react maybe missing something obvious. I'm getting the data when I console log all good.
code below:
import React, { Component } from 'react'
import axios from 'axios';
class Contacts extends Component {
constructor(){
super();
this.state = {
contacts: [],
}
}
componentDidMount(){
axios.get('url')
.then(response => {
this.setState({ contacts: response.data });
})
.catch(function (error) {
console.log(error);
})
}
render() {
const { contacts } = this.state
return(
<div>
{contacts.map(contact => (
<h1>contact.hello</h1>
))}
</div>
)
}
}
export default Contacts;
Apparently its an object not an array...
How can i render this object then?
It has one property for now but will have more later on: tried JSON.stringify(obj)
{hello: "test"}
The problem is that you set contacts to response.data, which evidently it's not an array.
componentDidMount fires after the component is mounted and tries to get the string 'url'. When state is updated, the component is redrawn and it gives the error.
Since the contacts is an object I would recommend you to do Object.keys and then .map on it so that you can get object keys and it’s values.
One more thing never forget to add unique key to the parent jsx element when you iterate array of data or an object like below.
<div>
{Object.keys(contacts).map((name, index) => (
<h1 key={'Key'+index}>{contacts[name]}</h1>
))}
</div>
From react docs:
Note:
These methods are considered legacy and you should avoid them in new code:
UNSAFE_componentWillMount()
When you want to wrap an object you can simply wrap it in brackets
class Contacts extends Component {
constructor() {
super();
this.state = {
contacts: [],
}
}
componentDidMount() {
axios.get('url')
.then(({ data }) => {
this.setState({ contacts: [data] });
})
.catch((error) => {
console.log(error);
});
}
render() {
const { contacts } = this.state;
return (
<div>
{contacts.map(contact => (
<h1 key={/* unique key */}>contact.hello</h1>
))}
</div>
);
}
}
Use async await to get the response before the component is mounted
import React, { Component } from 'react'
import axios from 'axios';
class Contacts extends Component {
constructor(){
super();
this.state = {
contacts: [],
}
}
async componentWillMount(){
const response = await axios.get('url')
this.setState({ contacts: response.data })
}
render() {
const { contacts } = this.state
return(
<div>
{contacts.map(contact => (
<h1>contact.hello</h1>
))}
</div>
)
}
}
export default Contacts;

How to set state in componentDidMount based on the response from a callback

I am basically trying to set the state based on the response I got from an API.
api.js
const baseURL = "http://localhost:8000";
export const getStatus = (list) => {
fetch(`${baseURL}/api/status`).then(res => {
return res.json();
}).then(status => {
list.setState({status: status});
});
};
And this is how I call it from a component
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
componentDidMount() {
getStatus(this);
}
I feel like it is not a good practice to pass this down and modify the state from the downstream api file. Is there a more "react" way to do this?
I also tried another way, which is to wait for the callback to send back the response and then modify the state based on the response, but the setState function never gets executed in componentDidMount. If someone can direct me, that would be great!
api.js
const baseURL = "http://localhost:8000";
export const getStatus = () => {
fetch(`${baseURL}/api/status`).then(res => {
return res.json();
}).then(status => {
return status;
});
};
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
componentDidMount() {
getStatus((status) => {
this.setState({status: status});
})
}
Better way is to use .then() in componentDidMount
api.js
export const getStatus = () => {
return fetch(`${baseURL}/api/status`).then(res => {
return res.json();
});
};
yourComponent.jsx
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
componentDidMount() {
getStatus()
.then(status => {
this.setState({status: status});
})
}
Making an API call and setting the state of a component based on what the call returns is normal practice in react. You don't need to pass a reference to this down to getStatus and for that matter you don't need to pass anything to getStatus. Instead, chain then off of what is returned from getStatus.
componentDidMount() {
getStatus()
.then(status => {
this.setState({status});
})
}
it is also unnecessary to call constructor or super in your component. Simply write:
class List extends React.Component {
state = {
status: []
}
}
If you are using ES6, try the async function syntax to increase readability.
api.js
export const getStatus = () => fetch(`${baseURL}/api/status`);
yourComponent.jsx
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
async componentDidMount() {
const res = await getStatus()
this.setState({status: res.json()});
}
Also, you might not need to initialize the state, and you could remove the constructor if so.
I have a working code:
fetch(serviceUrl)
.then(result => result.json())
.then(newStatus => this.setState({status: newStatus}))
.catch(error => {
console.log(error);
this.setState({status: 'error'});
})

Categories

Resources