how to display a fetch result in a react app - reactjs

I have a "Banner" object that I want to use to display my text and I made a button to toggle whether the message shows or is hidden:
function App() {
function Banner(props) {
if (!props.warn) {
return null;
}
return (
<div className="message">
{this.state.message}
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showMessage: false, message: 'foo'};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showMessage: !state.showMessage
}));
}
componentDidMount() {
fetch(
"https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((json) => {
this.setState({
message: json,
DataisLoaded: true
});
})
}
render() {
return (
<div class="App">
<Banner warn={this.state.showMessage} />
<button onClick={this.handleToggleClick}>
{this.state.showMessage ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
return (
<div className="App">
<Page />
</div>
);
}
export default App;
Why is the text of the message variable not the text in the banner? If I replace the {this.state.message} with a string literal the banner displays it, but as the code is now when I press the button the page clears. Any ideas?

You should pass data to Banner and with useEffect and useState get and update the data when it changes
const { useEffect, useState } = React;
function Banner(props) {
const [message, setMessage] = useState('');
useEffect(() => {
setMessage(props.message);
}, [props]);
if (!props.warn) {
return null;
}
return <div className="message">{JSON.stringify(message)}</div>;
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
showMessage: false,
message: 'foo',
};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState((state) => ({
showMessage: !state.showMessage,
}));
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then((res) => res.json())
.then((json) => {
this.setState({
message: json,
DataisLoaded: true,
});
});
}
render() {
return (
<div class="App">
<Banner message={this.state.message} warn={this.state.showMessage} />
<button onClick={this.handleToggleClick}>
{' '}
{this.state.showMessage ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
function App() {
return (
<div className="App">
<Page />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

I have a JSON file listed in React, I want onclick to open another page with more details about that item (using id)

Here I have the JSON list file displayed.
import React, { Component} from 'react'
import {Link} from 'react-router-dom';
class KitchenTables extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
}
}
componentDidMount() {
fetch('http://localhost:8080/kitchen/getAll/')
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json,
})
})
}
render() {
var { isLoaded, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>
} else {
return (
<div>
<div className="sell" id="sell">
<div className="sell__wrapper">
{items.map(item => (
<div className="sellCard__container">
<div className="sell__card" >
<img src= { `data:image/png;base64,${item.image}` } className="imgContainer" />
<img src={ `data:image/png;base64,${item.image2}` } className="img-hover imgContainer" />
</div>
<a className="product__text" href="">
<h2 key="id=18">{item.name}</h2>
<p className="product__text__description"> { item.description } </p>
<p>Price: <span>{item.price}</span> €</p>
</a>
</div>
))}
This is the page where I want to display full details.
Here is where I want to display full details. When I click on a card in KitchenTables, to Link to Submit page, and display all details by id.
export default class Submit extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
}
}
componentDidMount() {
fetch('http://localhost:8080/kitchen/getAll/23')
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json,
})
})
}
render() {
var { isLoaded, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>
} else {
return (
<div>
{items.map(item => (
))}

React Api call: Displaying only 1st onSubmit but nothing after

I have two components, CryptoPrice with a coin prop which calls an API to get the price, and Nav where I search for a coin, and it renders the CryptoPrice component assigning the onSubmit value to CryptoPrice coin prop.
The display works good until I do a second onSubmit from the Nav. When I do a second onSubmit, nothing changes.
App.js code:
import CryptoPrice from "./components/CryptoPrice";
import Nav from "./components/Nav";
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Crypto Prices</h1>
<div className="flex">
<CryptoPrice coin="bitcoin" />
<CryptoPrice coin="ethereum" />
</div>
<div>
<Nav></Nav>
</div>
</header>
</div>
);
}
CryptoPrice component:
import styles from "./css/CryptoPrice.module.css";
export default class CryptoPrice extends React.Component {
constructor(props) {
super(props);
this.state = {
price: [],
url: `https://api.coingecko.com/api/v3/simple/price?ids=${this.props.coin}&vs_currencies=usd`,
};
}
componentDidMount = () => {
this.loadData();
setInterval(this.loadData, 20000);
};
loadData = () => {
fetch(this.state.url)
.then((response) => response.json())
.then((data) => {
let key = Object.keys(data);
return data[key];
})
.then((coin) => {
let price = coin.usd;
this.setState({ price });
});
};
render() {
return (
<div className={styles.padding}>
<h2>{this.props.coin} price</h2>
<div>{this.state.price}$</div>
</div>
);
}
}
Nav Component
import CryptoPrice from "./CryptoPrice";
export default class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
coin: "",
isSubmitted: false,
};
}
componentDidMount() {
this.setState({ isSubmitted: false });
}
render() {
return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
this.setState({ isSubmitted: true });
}}
>
<input
type="text"
onChange={(e) => {
this.setState({ coin: e.target.value });
}}
></input>
<input type="submit" value="Add"></input>
</form>
{this.state.isSubmitted && <CryptoPrice coin={this.state.coin} />}
</div>
);
}
}
Thanks so much for any help/feedback
Your issue is because you are setting the url in state so it will not update when the props update. Try changing you fetch function to use props directly(also remember to clear the setInterval when you unmount):
loadData = () => {
fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${this.props.coin}&vs_currencies=usd`)
.then((response) => response.json())
.then((data) => {
let key = Object.keys(data);
return data[key];
})
.then((coin) => {
let price = coin.usd;
this.setState({ price });
});
};

I have a react app that should render the child component card on click. When trying to pass the child down from the parent, the parent breaks

I'm also using semantic-ui-react. When I pass the child component down from the parent the css styling gets all messed up, I lose my images and the click doesn't work.
I can call the cardClickHandler method in the parent component and am console logging the correct child, i just can't get it to render (am not hitting the console.log in the child component).
I also tried to run the cardClickHandler method in the images container to pass it down but that didn't work.
please help and explain what i'm doing wrong. thanks!
images container:
import React from 'react';
import SearchBar from '../components/SearchBar';
import Images from '../components/Images';
import ImageCard from '../components/ImageCard';
class ImagesContainer extends React.Component {
state = {
images: [],
image: {},
sortValue: '',
inputValue: '',
};
componentDidMount() {
fetch('http://localhost:3000/images').then((resp) => resp.json()).then((resp) => {
this.setState({
images: resp
});
});
}
imageFilterOnChange = (event) => {
this.setState({
inputValue: event.target.value
});
};
sortImages = (images) => {
if (this.state.sortValue === 'location') {
return [ ...images ].sort((a, b) => {
if (a.location > b.location) {
return 1;
} else if (a.location < b.location) {
return -1;
} else {
return 0;
}
});
} else {
return images;
}
};
render() {
const filteredImages = this.state.images.filter((image) => {
return image.location.toLowerCase().includes(this.state.inputValue.toLowerCase());
});
return (
<div>
<Images
images={this.sortImages(filteredImages)}
onClick={this.cardClickHandler}
/>
<SearchBar
images={this.sortImages(filteredImages)}
imageFilterOnChange={this.imageFilterOnChange}
inputValue={this.state.inputValue}
onChange={this.handleSortImages}
/>
</div>
</div>
);
}
}
export default ImagesContainer;
parent component:
import React from 'react';
import ImageCard from './ImageCard';
import { Card, Image } from 'semantic-ui-react';
class Images extends React.Component {
state = {
image: []
};
cardClickHandler = (e) => {
let cardId = e.target.dataset.id;
this.props.images.find((image) => {
return image.id === cardId;
});
console.log('hi, cardId', cardId);
fetch(`http://localhost:3000/images/${cardId}`)
.then((resp) => resp.json())
.then((resp) => {
this.setState({
image: resp
})
console.log(this.state.image);
})
}
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<Card
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</Card>
);
});
}
}
export default Images;
child component:
i'm not hitting the console.log here, so no more code!
import React from 'react';
import { Card, Image } from 'semantic-ui-react';
class ImageCard extends React.Component {
render() {
console.log('image card');
return (
<Card>
</Card>
);
}
}
export default ImageCard;
I left a comment with a few improvements to the code you could make. Specifically:
You have an extra </div> in your ImagesContainer.
Also, you'll want to remove onClick={this.cardClickHandler} from ImagesContainer as cardClickHandler is defined not on ImagesContainer but instead on your Images component.
But the problem is that you are not rendering your ImageCard component at all. You are just rendering <Card> instead of <ImageCard>
Specifically, your parent component's render should change from this:
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<Card
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</Card>
);
});
}
to this:
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<ImageCard
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</ImageCard>
);
});
}

React Router/Context API Search Component

I used to make this code work out for my search component but after the on submit is called, I receive this error which never happened before, does anyone have any clue???
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
import React, { Component } from "react";
import axios from "axios";
import { Redirect } from "react-router-dom";
import { Consumer } from "../context";
class Search extends Component {
constructor() {
super();
this.state = {
productTitle: "",
apiUrl: "*******************************",
redirect: false
};
}
findProduct = (dispatch, e) => {
e.preventDefault();
axios
.post(
`${this.state.apiUrl}`,
JSON.stringify({ query: this.state.productTitle })
)
.then(res => {
dispatch({
type: "SEARCH_TRACKS",
payload: res.data.output.items
});
this.setState({ items: res.data.output.items, redirect: true });
})
.catch(err => console.log(err));
};
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
render() {
const { redirect } = this.state;
if (redirect) {
return <Redirect to="/searchresult" />;
}
return (
<Consumer>
{value => {
const { dispatch } = value;
return (
<div>
<form onSubmit={this.findProduct.bind(this, dispatch)}>
<div className="form-group" id="form_div">
<input
type="text"
className="form-control form-control-md"
placeholder="...محصولات دسته یا برند مورد نظرتان را انتخاب کنید"
name="productTitle"
value={this.state.productTitle}
onChange={this.onChange}
/>
<button className="btn" type="submit">
<i className="fas fa-search" />
</button>
</div>
</form>
</div>
);
}}
</Consumer>
);
}
}
import React, { Component } from 'react'
import axios from 'axios'
const Context = React.createContext();
export const axiosDashboard = () => {
const URL = (`*****************`);
return axios(URL, {
method: 'POST',
data: JSON.stringify({refresh:"true"}),
})
.then(response => response.data)
.catch(error => {
throw error;
});
};
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_TRACKS':
return {
...state,
items: action.payload,
heading: 'Search Results'
};
default:
return state;
}
}
export class Provider extends Component {
state = {
dispatch:action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer
import React, { Component } from 'react'
import { Consumer } from '../context'
import SearchResult from './SearchResult'
import './Search.css'
class Tracks extends Component {
render() {
return (
<Consumer>
{value => {
const { items } = value
if(items === undefined || items.length === 0){
return 'hello'}
else{
return(
<React.Fragment>
<div id='products_search'>
<div className='container'>
<div className="row justify-content-end">
{items.map(item => (
<SearchResult
key={item.id}
id={item.id}
title={item.name}
current_price={item.current_price}
lowest_price={item.lowest_price}
store_name={item.store_name}
thumb={item.thumb_url}/>
))}
</div>
</div>
</div>
</React.Fragment>
)
}
}}
</Consumer>
)
}
}
export default Tracks
import React from 'react'
import {Link} from 'react-router-dom'
import './Search.css'
const SearchResult = (props) => {
const {title,current_price,lowest_price,thumb,id,store_name} = props
return (
<div className="col-md-3" id="searchresult">
<img src={thumb} alt=""/>
<div className="sexy_line"></div>
<p className="muted">{store_name}</p>
<h6>{title}</h6>
<p>{lowest_price}</p>
<Link to={`products/item/${id}`}>
<button type="button" className="btn btn-light rounded-pill">{
new Intl
.NumberFormat({style: 'currency', currency: 'IRR'})
.format(current_price)
}</button>
</Link>
</div>
)
}
export default SearchResult
Maximum update depth exceeded.
This means that you are in a infinit loop of re rendering a component.
The only place where I can see this is possible to happen is in this part
if (redirect) {
return <Redirect to="/searchresult" />;
}
Maybe you are redirecing to the a route that will get the same component that have the redirect.
Please check if you aren't redirecting to the same route as this component and provide your routes and what is inside Consumer.

ReactJS error TypeError: Cannot read property 'map' of undefined

I'm attempting to consume a JSON API using fetch; the error mentioned above appears on the following line: **this.state.data.map( (dynamicData,key)=>**
This is my ReactJS code with the error line in bold:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
//constructor
constructor() {
super();
this.state = {
data: [],
}
} //end constructor
componentDidMount(){
return fetch('https://jsonplaceholder.typicode.com/todos')
.then((response)=>response.json())
.then((responseJson)=>
{
this.setState({
data:responseJson.todos
})
console.log(this.state.data)
})
} // end component did mount
render() {
return (
<div>
<h2>Todo:</h2>
<div>
{
**this.state.data.map( (dynamicData,key)=>**
<div>
<span> {dynamicData.userId} </span>
<span> {dynamicData.id} </span>
</div>
)
}
</div>
</div>
);
}
}
export default App;
Could I get some help as to what I'm doing wrong? Thanks in advance
import React, { Component } from "react";
import { render } from "react-dom";
class App extends Component {
state = {
data:[],
url: "https://jsonplaceholder.typicode.com/todos"
};
componentDidMount() {
fetch(this.state.url)
.then(response => response.json())
.then(data => this.setState({ data }));
}
render() {
const { data } = this.state;
data && console.log(data);
return (
<div>
{data &&
data.map(item => <div> Hello User With Id: {item.userId} </div>)}
</div>
);
}
}
render(<App />, document.getElementById("root"));
Your didMount should look like mine also, setState takes a callback so if you wanted to see what the data looked like it would be like this
this.setState({ data }, () => console.log(this.state.data))
In your render it looks like you forgot the parenthesis after the arrow function in map.
render() {
return (
<div>
<h2>Todo:</h2>
<div>
{
this.state.data.map((dynamicData,key)=> (
<div>
<span> {dynamicData.userId} </span>
<span> {dynamicData.id} </span>
</div>
))
}
</div>
</div>
);
}

Resources