setState from .map inside componentDidMount() - javascript

I am trying to find a solution to using setState on mapped items inside componentDidMount.
I am using GraphQL along with Gatsby with many data items returned but require that on specific pathname is === to slug the state is updated in the component to the matching littleHotelierId.
propertyInit = () => {
const pathname = location.pathname;
return (
<StaticQuery
query={graphql`
query {
allContentfulProperties {
edges {
node {
id
slug
information {
littleHotelierId
}
}
}
}
}
`}
render={data => {
data.allContentfulProperties.edges.map(({ node: property }) => {
if (pathname === property.slug) {
!this.isCancelled &&
this.setState({
littleHotelierId: property.information.littleHotelierId
});
}
return null;
});
}}
/>
);
};
Then I am pulling this into componentDidMount as
componentDidMount() {
this.propertyInit();
}
not relevant but as reference this.isCancelled = true; is added to componentWillUnmount.
I don't receive any errors but if I console.log(littleHotelierId) I get nothing.
I did at first think that it may be because return is null so tried giving the map a const and returning as
render={data => {
data.allContentfulProperties.edges.map(({ node: property }) => {
if (pathname === property.slug) {
const littleHotelier =
!this.isCancelled &&
this.setState({
littleHotelierId: property.information.littleHotelierId
});
return littleHotelier;
}
});
}}
but this was unsuccessful too.
The Goal is for componentDidMount to map items returned in the GraphQL data as
componentDidMount() {
if (path1 === '/path-slug1') {
!this.isCancelled &&
this.setState({
littleHotelierId: 'path-id-1'
});
}
if (path2 === '/path-slug2') {
!this.isCancelled &&
this.setState({
littleHotelierId: 'path-id-2'
});
}
... // other items
}
I think the issue is that GraphQL is fetching data as asynchronous and this request not completed as componentDidMount() is called. If I console.log the data it is not returning anything to the console. How can I fix this?

I think you need to create some filtered data as a result of a map function. After you have filtered data you do setState({data: data}). It is not good to do multiple setState.
If your GraphQL returns promise then you can write something like the following:
componentDidMount() {
this.fetchData()
.then(data => {
const filteredData = data.filter(element =>
element.someProperty === propertyValue
);
this.setState({ data: filteredData });
})
}

Related

How to update the FlatList dynamically in react native?

Initially loading data from API to FlatList using setState and it loaded perfectly. But I have to perform some actions like create, update & delete of FlatList row. When I try to add new data to the FlatList, the data is not rendered in FlatList with an updated one, but In API it's updated.
How to re-render the flatlist after updating to the API and load the new data to FLatList?
Here is my code:
constructor(props) {
super(props);
this.state = {
faqs: [],
}
this.loadFaq();
};
To load the data to FlatList from the API:
loadFaq = async () => {
let resp = await this.props.getFaqGroup();
if (resp.faqs) {
console.log(resp.faqs)
this.setState({
faqs: resp.faqs,
// refresh: !this.state.refresh
})
}
};
To add new data to API:
createFaqGroup = async (name) => {
let resp = await this.props.createFaqGroup(name);
// console.log("resp", resp)
// this.setState({
// refresh: !this.state.refresh
// })
// this.forceUpdate();
this.closePanel();
}
FlatList code:
{this.state.faqs && <FlatList
extraData={this.state.faqs}
horizontal={false}
data={this.state.faqs}
contentContainerStyle={{ paddingBottom: 75 }}
renderItem={({ item: faqs }) => {
return <Card gotoQuestionList={this.gotoQuestionList} key={faqs._id} faqs={faqs} openPanel={(selectedFaq) => this.openPanel({ name: selectedFaq.name, id: selectedFaq._id })} deletePanel={(selectedFaq) => this.deletePanel({ name: selectedFaq.name, id: selectedFaq._id, isPublished: selectedFaq.isPublished })}></Card>
}
}
keyExtractor={(item) => item._id}
/>}
this.props.createFaqGroup function code:
export const createFaqGroup = (name) => {
const options = {
method: 'POST',
data: { "name": name },
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${store.getState().auth.info.token}`
}
};
return async (dispatch) => {
console.log('url::', options)
try {
let url = `${config.baseUrl}${config.faqUrl}`;
let resp = await axios(url, options);
console.log(resp.data)
return resp && resp.data ? resp.data : null;
} catch (error) {
alert(error)
if (error.response && error.response.status === 401) {
dispatch({
type: type.ERROR,
data: error.response.data
});
} else {
dispatch({
type: type.CREATE_FAQ_GROUP_ERROR,
error: error.message
});
}
}
};
}
Any help much appreciated pls...
Flatlist will update automatically when you set your state i.e by using this.setState() function, it means whenever any changes made to your state variable it will rerender your flatlist. if you still face the same problem remove your this.state.faqs && part, this looks unnecessary because there is no need to check if you are passing the empty array to faltlist or not, flatlist allows you to pas empty array as well, it will not give you any error.
I think you should load data again, after you add them, so you can modify your function createFaqGroup like this:
createFaqGroup = async (name) => {
let resp = await this.props.createFaqGroup(name);
this.loadFaq();
this.closePanel();
}
Try this:
createFaqGroup = async (name) => {
let resp = await this.props.createFaqGroup(name);
this.setState({faqs: [...this.state.faqs, name]})
this.closePanel();
}

How to set state of cart when no items?

In my E-Commerce project, the products are stored in localStorage. componentDidMount() gets all these products from localStorage and displays it. How to state the state or condition when no products are available.
componentDidMount = () => {
this.setProducts();
// Gets all products in cart from localStorage
this.setState(
() =>{
return {cart: JSON.parse(localStorage.getItem('myCart'))}
},
() => {
this.addTotal()
})
// How to set the condition when no products in Cart?
}
// Set all the products on Main Page
setProducts = () => {
let tempProducts = [];
storeProducts.forEach(item => {
const singleItem = {...item};
tempProducts = [...tempProducts, singleItem];
});
this.setState(() => {
return {products: tempProducts};
});
};
// Here products are added to cart and stored in localStorage
addToCart = (id) => {
let tempProducts = [...this.state.products];
const index = tempProducts.indexOf(this.getItem(id));
const product = tempProducts[index];
product.inCart = true;
product.count = 1;
const price = product.price;
product.total = price;
this.setState(() => {
return { products: tempProducts, cart: [...this.state.cart,
product] };
},
() => {
this.addTotal();
localStorage.setItem('myCart', JSON.stringify(this.state.cart))
});
}
I have also tried to make following changes, but, no effect. In componentDidMount()
componentDidMount() {
if(this.state.cart.length > 0) {
this.setState(
() =>{
return {cart: JSON.parse(localStorage.getItem('myCart'))}
},
() => {
this.addTotal()
})
} else {
this.setState(() => {
return {cart:[]}
})
}
}
// Clear Cart
clearCart = () => {
this.setState(() => {
return {cart:[]}
}, () => {
this.setProducts();
this.addTotal();
})
localStorage.removeItem('myCart')
}
When I remove code of setState (shown in the beginning) from componentDidMount() displays empty cart message, which is expected else, if the cart is cleared and refreshed browser throws 'cart.length' error. Any possible solution?
JSON.parse will return an object. It depends on your data structure but there is no cart.lendth for the object. So that is your first problem. So for the below example, I store the parsed value as an array.
Also, if state.cart is not initiated, there is no .length property for it.
For your second problem have a look at the below version of your componentDidMount:
componentDidMount() {
if(Array.isArray(this.state.cart) && this.state.cart.length) {
const cart = localStorage.getItem('myCart') || [];
this.setState({cart: [JSON.parse(cart)]}), this.addTotal);
} else {
this.setState({ cart:[] });
}
}
Again, it depends on your implementation, but you might need to initiate the component's state with cart: localStorage.getItem('myCart') || [] or doing what I have done above. I'm basically checking if cart is an array && it has length then parse it otherwise initiate the array.
Finally I got the solution as below
const cart = localStorage.getItem('myCart')
this.setState({cart: JSON.parse(cart) ? JSON.parse(cart) : []}, this.addTotal)
Just modified the code and works perfectly without any issues

Getting backend data in front reactJS

I am getting data from the backend to display it in the font like this
componentDidMount() {
const response = this.props.store.privateImputationData;
console.log(response);
}
It displays null in the console, now if i do a setTimeout it works!
componentDidMount() {
setTimeOut(() => {
const response = this.props.store.privateImputationData;
console.log(response);
}, 500);
}
This how i m getting data from my store:
#computed get ImputationData() {
return this.privateImputationData || {};
}
loadImputation = (diplayedImputations) => {
HttpClient.postJSON(this.apiDataUrl, diplayedImputations).then((result) => {
this.privateImputationData = result;
this.loadAdditionalData();
});
}
How can i do it without setTimeout?
You can use the state object: State and Lifecycle. Whenever the state changes, whatever component uses it, get's updated too.
this.state = {privateImputationData: null} //or some default
So in your code:
#computed get ImputationData() {
return this.privateImputationData || {};
}
loadImputation = (diplayedImputations) => {
HttpClient.postJSON(this.apiDataUrl, diplayedImputations).then((result) => {
this.setState({privateImputationData: result});
this.loadAdditionalData();
});
}
To use the value:
this.state.privateImputationData;

editable json and setState in react

I try to display bunch of json object using map within a textarea. User can edit it, but I have to validate all the json is valid before it get pass to my server.
I'm having problem parsing it, where should I parse it? Parsing it on the onChange it a way but it's dangerous.
onChange = (e, idx) => {
this.setState({
data: this.state.data.map((o,i) => {
if(i === idx){
return JSON.parse(e.target.value) //dangerous
}
return o
})
})
}
https://codesandbox.io/s/880414y0m0
You can do something like this:
onChange = (e, idx) => {
let dataToSet = [...this.state.data]
let error = false
try {
dataToSet = dataToSet.map((o,i) => {
if(i === idx){
return JSON.parse(e.target.value) //dangerous
}
return o
})
} catch(e) {
error = true
}
this.setState({
data: dataToSet,
error,
})
}
Here is the codesandbox link which handles those scenario. codesandbox

persist firebase data in react-native app

I am trying to build my first app on react-native, but I have problem with that. I want to show my data from firebase and also the data which insert to firebase does not get deleted when I reload the app. That's what I have in my render:
let tags = this.state.tagArray.map((val, key) => {
return <TagContainer key={key} keyval={key} val={val}
deleteMethod={() => this.deleteTag(key)} />
});
this is my firebase config:
this.itemRef=this.getRef().child('tags');
getRef(){
return firebaseApp.database().ref();
}
getItems(itemRef){
itemRef.on('value',(snap)=>{
let items=[];
snap.forEach((child) => {
items.push({
title:child.val().title,
_key:child.key
});
});
Array.prototype.push.apply(this.state.tagArray, items);
this.setState({ tagArray: this.state.tagArray })
});
this my addtag function where I set my tagArray:
addTag() {
if (this.state.tagText) {
this.state.tagArray.push({
'tag': this.state.tagText
});
this.setState({ tagArray: this.state.tagArray })
this.setState({ tagText: '' })
this.itemRef.push({title:this.state.tagText});
}
}
You are not supposed to push data into state object. No state changes are allowed without using setState().
addTag() {
if (this.state.tagText) {
let tagArray = [...this.state.tagArray,{'tag': this.state.tagText}]
this.setState({ tagArray: tagArray,tagText: '' }, ()=>{
this.itemRef.push({title:this.state.tagText});
})
}
}

Categories

Resources