React setstate does not work in IE11 after render is called - reactjs

I am new to react development and I have a react app where on the componentDidMount am setting the state of value as "add" and it renders the div content for "add" and once button click on the add div am calling an addstate method
where am setting the state of the value as "edit" and it renders the div content with respect to "edit" and where i call again the addstate method through done method call.
In this case the fetch call from addstate method is happening to the backend but the state is not setting back to edit..it fails only in IE11. It works on chrome, firefox and mobile devices.
If i remove the piece of code "Value:edit" in addstate method its working good. But my requirement needs to render based upon different scenarios. so basically am able to set the state of the result only once in IE11. it does not work repeatedly.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "test",
items: []
}
};
addState() {
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
this.setState({
value: "edit",
items: result
});
}
)
.catch(error => console.error('Error:', error));
}
done() {
this.setState({
value: "add"
});
}
componentDidMount() {
fetch("/local/getThings")
.then(
(result) => {
this.setState({
value: "add"
});
}
)
.catch(error => console.error('Error:', error));
}
render() {
const { value, items } = this.state;
if (value === "add") {
return <div >
<div >
<ul >
<li onClick={() => this.addState()}>
<div>
<img src="Add.png" />
<center><p>AddButton</p></center>
</div>
</li>
</ul>
</div>
</div>
;
}
if (value === "edit") {
return (<div>
<div >
<ul >
<li onClick={() => this.done()}>
<div >
<img src="Save.png" />
<center><p>SaveButton</p></center>
</div>
</li>
{items.map(item => (
<center><p>{item.name}</p></center>
</li>
))}
</ul>
</div>
</div>
);
}
}
}
ReactDOM.render(React.createElement(App, null), document.getElementById("details")); ```

Try binding your method and refer to it directly in your onClick event:
lass App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "test",
items: []
}
this.addState = this.addState.bind(this);
};
addState() {
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
this.setState({
value: "edit",
items: result
});
}
)
.catch(error => console.error('Error:', error));
}
done() {
this.setState({
value: "add"
});
}
componentDidMount() {
fetch("/local/getThings")
.then(
(result) => {
this.setState({
value: "add"
});
}
)
.catch(error => console.error('Error:', error));
}
render() {
const { value, items } = this.state;
if (value === "add") {
return <div >
<div >
<ul >
<li onClick={this.addState}>
<div>
<img src="Add.png" />
<center><p>AddButton</p></center>
</div>
</li>
</ul>
</div>
</div>
;
}
if (value === "edit") {
return (<div>
<div >
<ul >
<li onClick={() => this.done()}>
<div >
<img src="Save.png" />
<center><p>SaveButton</p></center>
</div>
</li>
{items.map(item => (
<center><p>{item.name}</p></center>
</li>
))}
</ul>
</div>
</div>
);
}
}
}

I added settimeout in my methods and it worked in IE. It seems like the response was slower in IE for the API calls. Not sure if there is a workaround or this is the right approach.
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
setTimeout(() => {
this.setState({
value: "edit",
items: result
});
}, 200);
}
)
.catch(error => console.error('Error:', error));
}```

Related

React API call is not working as intended with setState()

So here I'm fetching records by page. At mounting, I fetched page 1 and on next and prev button I'm changing the count, which is changing the page number and making a get request. But my code is not working correctly. It is giving me the result of count's previous state.
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {},
count: 1,
};
}
handleApi = () => {
fetch(`http://localhost:9000/accounts?page=${this.state.count}`)
.then((res) => res.json())
.then((res) => {
this.setState({ data: res });
})
.catch((err) => console.log(err));
};
handlePrev = () => {
if (this.state.count > 1) {
this.setState((prevState) => ({
count: prevState.count - 1
}))
this.handleApi();
}
};
handleNext = () => {
if (this.state.count < this.state.data.total) {
this.setState((prevState) => ({
count: prevState.count + 1
}))
this.handleApi();
}
};
componentDidMount() {
this.handleApi();
}
render() {
return (
<div className="App container">
<h1 className="mb-3 text-center">Pagination Homepage</h1>
{Object.keys(this.state.data).length !== 0 ? (
<ListData detail={this.state.data} />
) : (
<h4 className="mb-5 text-center">No Data to Show.</h4>
)}
<nav aria-label="Page navigation example">
<ul className="pagination justify-content-center mt-4">
<li className="page-item">
<button
className="btn btn-outline-primary me-3"
onClick={this.handlePrev}
>
Prev
</button>
</li>
<li className="page-item">
<button
className="btn btn-outline-success"
onClick={this.handleNext}
>
Next
</button>
</li>
</ul>
</nav>
</div>
);
}
}
export default App;
ListData.js
export default class ListData extends Component {
render() {
return (
<div className="list-outer">
<h4 className="mb-3 text-center"> List Data</h4>
<div className="list-head">
<p>Name</p>
<p>E-mail</p>
</div>
{this.props.detail.data &&
this.props.detail.data.map((list, index) => (
<div key={list._id} className="list">
<p className="list-item">{list.name.toUpperCase()} </p>
<p className="list-item"> {list.mail}</p>
</div>
))}
</div>
);
}
}
Console
After updating the count the API is still calling count's previous state.
Here page number in Data should be equal to the count.
Since setState works in an asynchronous way, after calling setState the this.state variable is not immediately changed. So if you want to perform an action immediately after setting state on a state variable and then return a result, use a callback:
handlePrev = () => {
if (this.state.count > 1) {
this.setState((prevState) => ({
count: prevState.count - 1
}), () => this.handleApi());
}
};
handleNext = () => {
if (this.state.count < this.state.data.total) {
this.setState((prevState) => ({
count: prevState.count + 1
}), () => this.handleApi());
}
};

When trying to create new item in MERN stack, getting TypeError: this.props.meals.map is not a function

I've created a MERN with redux application where users can order a meal from a menu. In the admin side, I am providing delete and add functions so the meals on the menu can be changed all in the same page. I have managed to get the delete meal item to work, but I am getting the following error when I try and add a new meal item:
My redux action is as follows:
export const createMeal = (meal) => (dispatch) => {
fetch("api/meals", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(meal),
})
.then((res) => res.json())
.then((data) => {
dispatch({ type: CREATE_MEAL, payload: data });
});
};
In my server file, I have the following endpoint created in Express:
app.post("/api/meals", async (req, res) => {
if (!req.body.title) {
return res.send({ message: "Data is required." });
}
const newMeal = new Meal(req.body);
const savedMeal = await newMeal.save();
res.send(savedMeal);
});
My UpdateMenuScreen is as follows:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMeals, deleteMeal, createMeal } from "../actions/mealActions";
class UpdateMenuScreen extends Component {
constructor(props) {
super(props);
this.state = {
meal: null,
showAddMenu: false,
title: "",
};
}
componentDidMount() {
this.props.fetchMeals();
}
componentDidUpdate() {
this.props.fetchMeals();
}
handleInput = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
createMeal = (e) => {
e.preventDefault();
const meal = {
title: this.state.title,
};
this.props.createMeal(meal);
};
deleteMeal(id) {
this.props.deleteMeal(id);
}
render() {
return (
<div>
<h3>Current Menu</h3>
{!this.props.meals ? (
<div>Loading...</div>
) : (
<ul className="meals">
{this.props.meals.map((meal) => (
<li key={meal._id}>
<div className="meal">
<p>{meal.title}</p>
<button
className="button"
onClick={() => this.props.deleteMeal(meal._id)}
>
Delete
</button>
</div>
</li>
))}
</ul>
)}
<button
onClick={() => {
this.setState({ showAddMenu: true });
}}
>
Add New Menu Item
</button>
{this.state.showAddMenu && (
<div className="cart">
<form onSubmit={this.createMeal}>
<ul className="form-container">
<li>
<label>Menu Item Title:</label>
<input
name="title"
type="text"
required
onChange={this.handleInput}
></input>
</li>
<li>
<button className="button primary" type="submit">
Save New Menu Item
</button>
</li>
</ul>
</form>
</div>
)}
</div>
);
}
}
export default connect((state) => ({ meals: state.meals.items }), {
fetchMeals,
deleteMeal,
createMeal,
})(UpdateMenuScreen);
Can anyone see what I'm missing? Or is it not possible to do this all on the same page?
EDIT:
I've console logged this.props.meals in ComponentDidMount and got the following results:
My mealsReducer is as follows:
const { FETCH_MEALS, DELETE_MEAL, CREATE_MEAL } = require("../types");
export const mealsReducer = (state = {}, action) => {
switch (action.type) {
case FETCH_MEALS:
return { items: action.payload };
case DELETE_MEAL:
return { items: action.payload };
case CREATE_MEAL:
return { items: action.payload };
default:
return state;
}
};
I also get this underneath my original error, could it be something in my mealActions that I don't have correct?
Please go to your reducer of meals and define the initial state of meals to []
This should fix your error.
render() {
const { meals = [] } = this.props // default to empty array when it's undefined
return (
<div>
<h3>Current Menu</h3>
{!meals.length ? (
<div>Loading...</div>
) : (
<ul className="meals">
{meals.map((meal) => (
<li key={meal._id}>
<div className="meal">
<p>{meal.title}</p>
<button
className="button"
onClick={() => this.props.deleteMeal(meal._id)}
>
Delete
</button>
</div>
</li>
))}
</ul>
)}
<button
onClick={() => {
this.setState({ showAddMenu: true });
}}
>
Add New Menu Item
</button>
{this.state.showAddMenu && (
<div className="cart">
<form onSubmit={this.createMeal}>
<ul className="form-container">
<li>
<label>Menu Item Title:</label>
<input
name="title"
type="text"
required
onChange={this.handleInput}
></input>
</li>
<li>
<button className="button primary" type="submit">
Save New Menu Item
</button>
</li>
</ul>
</form>
</div>
)}
</div>
);
}
}

Bootstrap v4 popover not updating on state change in React

I have a "Nightlife Coordination" app (from the Free Code Camp curriculum) that allows a user to search by city and RSVP to a bar for that night. The app keeps a list of who has RSVP'd and who is going. It is built with React and Bootstrap v4 (and Node on the back end).
I have text under each bar location that, when clicked, allows a user to RSVP or unRSVP. There is also a button that shows how many people have RSVP'd and, if clicked, will display a Bootstrap popover of the list of people who have RSVP'd.
If a user RSVPs (or unRSVPs), I want the list to update. (Currently, the number on the button DOES update, but not the list.)
The following two images show the problem:
Upon initial load, all is correctly functional
When the user RSVPS or unRSVPs, the number on the button correctly updates, but the list does not
Here is my code.
The list is being generated in the data-content attribute in the second anchor tag in the render method.
Can anyone help?
One other hint is that in my React developer tools Chrome extension, it shows the data-content attribute correctly updating upon RSVP and unRSVP. Is it that perhaps Bootstrap saves the contents of the data-content attribute in its JS file upon initial render and does not update it?
const React = require('react');
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {
countMeIn: false, // coming from Mongo
numberGoing: this.props.user_namesArr.length,
user_id: this.props.twitter_id,
user_name: this.props.user_name,
yelp_id: this.props.yelp_id,
user_namesArr: this.props.user_namesArr
};
}
componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
this.setState({
countMeIn: true
});
}
}
componentDidUpdate(prevProps, prevState) { // Need both in case user logs in after initial page load
console.log(this.state.user_namesArr);
if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
this.setState({
countMeIn: true
});
}
$('[data-toggle="popover"]').popover();
}
rsvp() {
let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let newArr = this.state.user_namesArr;
newArr.push(this.props.user_name);
this.setState({
numberGoing: this.state.numberGoing + 1,
countMeIn: true,
user_namesArr: newArr,
});
})
}
unrsvp() {
let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let ind = this.state.user_namesArr.indexOf(this.props.user_name);
let newArr = this.state.user_namesArr;
newArr.splice(ind, 1);
this.setState({
numberGoing: this.state.numberGoing - 1,
countMeIn: false,
user_namesArr: newArr,
});
})
}
render() {
return (
<div className="col-lg-4 onecomponent">
<a href={ this.props.bar_yelp_url } target="_blank">
<div className="barname text-center">
{ this.props.name }
</div>
<div className="priceline">
<img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
</div>
<div className="image">
<img class="mainimg" src={ this.props.image_url } />
</div>
<div className="address text-center">
{ this.props.loc[0] }., { this.props.loc[1] }
</div>
</a>
<hr/>
<div className="text-center">
<a tabindex="0" role="button" className="btn btn-success" data-toggle={ this.state.user_namesArr.length > 0 ? "popover" : "" } data-trigger="focus" title="Who's In?" data-content={ this.state.user_namesArr }>
{ this.state.numberGoing } going
</a>
{
this.props.loggedIn ?
this.state.countMeIn ?
<span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
<span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
<span> Please log in </span> // if not logged in
}
</div>
</div>
)
}
}
module.exports = Bar;
Maybe using ref could help ... but why not use reactstrap and more important why not react-popper ...? It's well known (https://github.com/FezVrasta/popper.js/#react-vuejs-angular-angularjs-emberjs-etc-integration) that many libraries doesn't work well with react or any other (virtual) DOM managers.
Do you really need jQuery?
Using react portals you can remove all theese dependencies.
It works with Reactstrap. I simply added reactstrap to my package.json file, and used the Reactstrap code.
const React = require('react');
import { Button, Popover, PopoverHeader, PopoverBody } from 'reactstrap';
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {
countMeIn: false, // coming from Mongo
numberGoing: this.props.user_namesArr.length,
user_id: this.props.twitter_id,
user_name: this.props.user_name,
yelp_id: this.props.yelp_id,
user_namesArr: this.props.user_namesArr,
popover: false
};
this.toggle = this.toggle.bind(this);
}
componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
this.setState({
countMeIn: true
});
}
}
componentDidUpdate(prevProps, prevState) {
if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
this.setState({
countMeIn: true
});
}
}
rsvp() {
let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let newArr = this.state.user_namesArr;
newArr.push(this.props.user_name);
this.setState({
user_namesArr: newArr,
numberGoing: this.state.numberGoing + 1,
countMeIn: true
});
})
}
unrsvp() {
let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let ind = this.state.user_namesArr.indexOf(this.props.user_name);
let newArr = this.state.user_namesArr;
newArr.splice(ind, 1);
this.setState({
user_namesArr: newArr,
numberGoing: this.state.numberGoing - 1,
countMeIn: false
});
})
}
toggle() {
this.setState({
popover: !this.state.popover
});
}
render() {
return (
<div className="col-lg-4 onecomponent">
<a href={ this.props.bar_yelp_url } target="_blank">
<div className="barname text-center">
{ this.props.name }
</div>
<div className="priceline">
<img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
</div>
<div className="image">
<img class="mainimg" src={ this.props.image_url } />
</div>
<div className="address text-center">
{ this.props.loc[0] }., { this.props.loc[1] }
</div>
</a>
<hr/>
<div className="text-center">
{ /* For this to work, id must have leading letters, otherwise throws massive errors. See here: https://stackoverflow.com/questions/23898873/failed-to-execute-queryselectorall-on-document-how-to-fix */ }
<Button id={ "abc" + this.props.yelp_id } className="btn btn-success" onClick={ this.toggle }>{ this.state.numberGoing } going</Button>
<Popover placement="right" isOpen={ this.state.popover } target={ "abc" + this.props.yelp_id } toggle={ this.toggle }>
<PopoverHeader>Who's In?</PopoverHeader>
<PopoverBody>{ this.state.user_namesArr }</PopoverBody>
</Popover>
{
this.props.loggedIn ?
this.state.countMeIn ?
<span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
<span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
<span> Please log in </span> // if not logged in
}
</div>
</div>
)
}
}
module.exports = Bar;

React - Map content inside a div

Good Morning! Why does my map content stay outside the "blog--div" div?
It's getting loose on Body and I do not know why. Help-me, please!
I try to put a border around the contents of the "blog--div" but the content becomes loose, making it impossible to apply styles.
imports[...]
class Blog extends Component {
constructor(props) {
super(props)
this.state = {
post: [],
}
}
componentDidMount() {
this.setState({ isLoading: true })
fetch(`${API}`)
.then(res => res.json())
.then(res => {
this.setState({
post: [res],
isLoading: false,
})
})
}
render() {
const { isLoading } = this.state
if (isLoading) {
return <Loading />
}
return (
<div className="blog">
<p className="blog__title">Blog</p>
{this.renderBlog()}
</div>
)
}
renderBlog() {
const page = this.state.post.map((post, key) => {
return (
<div className="blog--div" key={key}>
<div className="blog__post">
<div className="blog__post--title">
<p><a target="_blank" rel="noopener noreferrer" href={post[0].link}>{post[0].title.rendered.replace('Visit.Rio', 'Projeto 1')}</a></p>
</div>
<div className="blog__post--description">
<p>{post[0].excerpt.rendered.replace('Visit.Rio', 'Projeto 1')}</p>
</div>
</div>
</div>
)
})
return page
}
}
export default Blog

Highlight item onClick - React.js

Add underscore to category-item onClick and remove underscore for any other item. Found some answers on how to do this with only two components, a "item-container-component" and "item-components". But i have three components involved. This is what I hope to achieve:
Archive-component (mother component):
class Archive extends React.Component {
constructor(props){
super(props);
this.state = {
products: [],
category: "",
activeIndex: 0
}
this.filterHandler = this.filterHandler.bind(this);
}
filterHandler(tag, index){
console.log('INDEX: ' + index);
this.setState({
category: tag,
activeIndex: index
})
}
componentDidMount(){
const myInit = {
method: "GET",
headers: {
"Content-Type": "application/json"
}
};
fetch("/getProducts", myInit)
.then((res) => {
return res.json();
})
.then((data) => {
this.setState({products:data});
})
.catch(function(err) {
console.log('ERROR!!! ' + err.message);
});
}
render() {
return (
<div>
<Menu />
<div className="archive-container">
<div className="archive-wrapper">
<CategoryContainer
filterHandler={this.filterHandler}
products={this.state.products}
activeIndex={this.state.activeIndex}
/>
<br/><br/>
<ProductContainer
products={this.state.category.length
? this.state.products.filter((prod) => prod.category === this.state.category)
: this.state.products.filter((prod) => prod.category === 'Paint')
}
/>
</div>
</div>
<Footer />
</div>
);
};
};
Category-container component:
class CategoryContainer extends Component {
render(){
const categories = [...new Set(this.props.products.map(cat => cat.category))];
return (
<div>
<ul className="filterList">{
categories.map((cat, index) =>
<CategoryItem
key={index}
index={index}
category={cat}
active={index === this.props.activeIndex}
handleClick={() => this.props.filterHandler(cat, index)}
/>
)
}</ul>
</div>
);
};
};
Category-item component:
class CategoryItem extends Component {
render(){
return (
<div>
<li
className={this.props.active ? 'active' : 'category'}
onClick={this.props.handleClick}
>
{this.props.category}
</li>
</div>
);
};
};
Yelp!
M
Suppose you have a li tag for which you want to change the color of.
you could probably try something like this.
<li id="colorChangeOnClick" class="redColor" onclick={this.onClickFunction()}></li>
Then in your react class you can have the on click function with parameters e:
onClick(e) {
//This would give you all the field of the target
console.log(e.target.elements);
// you can do all sorts of Css change by this way
e.target.element.class="newGreenColor";
}
Also make sure to make a state or a prop change otherwise the page would not render again.

Resources