I have written my code where I must Fetch data from API on button, but for more easier and cleaner page I want to have toggle button, but i dont know how to implement it in my function and into my render method.
I tried with state, and function, but dont know how to write it in render.
import React from "react";
import "./FetchBeerStyle.css";
export default class FetchBeer extends React.Component {
constructor(props) {
super(props);
this.state = {
beers: [],
on:false,
}
}
handleClick = () => {
fetch('https://api.punkapi.com/v2/beers')
.then(res => {
if (!res.ok) {
throw new Error('There has been an error');
}
return res.json();
})
.then(data => {this.setState({ beers: data})
console.log(this.state.beers);
})
.catch(e => console.log(e))
}
render(){
return (
<div>
<button onClick={this.handleClick}>Get All Beers/Return</button>
{this.state.beers.map((beer) => {
return <div key={beer.id}>
<h1 className="h1" >NAME : {beer.name}</h1>
<img src= {beer.image_url}/>
<h2>TAGLINE : {beer.tagline}</h2>
<p>FIRST BREWED : {beer.first_brewed}</p>
<p> DESCRIPTION : <br></br>{beer.description}</p>
<p> FOOD PAIRING : <br></br>{beer.food_pairing}</p>
<p> ALCOHOL BY VOLUME(%) : {beer.abv}</p>
<p> pH : {beer.ph}</p>
</div>
})}
</div>
);
}
}
You can check if button is true or false then you render your data. Also you need to call a different function that changes you button's state on click and call you function "handleClick" to fetch the data.
`{
this.state.on ?
this.state.beers.map((beer) => {
return <div key={beer.id}>
<h1 className="h1" >NAME : {beer.name}</h1>
<img src= {beer.image_url}/>
<h2>TAGLINE : {beer.tagline}</h2>
<p>FIRST BREWED : {beer.first_brewed}</p>
<p> DESCRIPTION : <br></br>{beer.description}</p>
<p> FOOD PAIRING : <br></br>{beer.food_pairing}</p>
<p> ALCOHOL BY VOLUME(%) : {beer.abv}</p>
<p> pH : {beer.ph}</p>
</div>
})
: null
}`
Related
I have the below api call from React Js. It is a class component. am new to React Js.
class UserComponent extends React.Component {
constructor(props){
super(props)
this.state=({
users: []
});
retrieve(e){
e.preventDefault();
fetch(`http://localhost:8080/test/{this.state.id}`,{
"method":"GET",
"headers":{
"accept":"application/json"
}
})
.then(response => response.json())
.then(response => {
this.setState({
users:response
})
}
render(){
return (
<div>
<form>
<table>
<tbody>
{
this.state.users === '' || null ? '' : Object.entries(this.state.users).map([key,value])=>(
Object.entries(key).map(field=>(
<tr>
<td>
<input name = "firstName" id = firstName" defaultValue = {field.firstName} required/>
</td>
</tr>
))))
}
</tbody>
</table>
</form>
</div>
)
}
}
export default UserComponent
I need to get the firstName and lastName and other details from a simple json file
{
"id":"12224343",
"depId":"1",
"employees":[{
"empId":"1",
"firstName":"sample",
"lastName":"test",
"address":[{
"street":"1",
"pin":"12345"
}]
}]
}
I need to get the firstname and last name of this nested object and address's object's value also. Is there any way like flatmap kind of thing is available in react class component. I didn't use useEffects and all.
when I tried to iterate it am getting "map is not a function. it is undefined"
the class component with state objects have any restrictions. tried a lot of options.
I need to iterate the object of object and fetch only particular values in the UI test box. like first name and lastname. now it is iterating all and displaying all in the UI. which looks messy.
Please help me to get a solution for this.
Since you initialized state users as an array. You can just use users.map(item). Each item contains multiple employees. You can also convert data from the current json file to any data structure which is comfortable for you to display in the table.
const {users} = this.state
users?.map((user,index) => {
return (
<div key={key.id}>
{user?.employees.map(item => {
return (
<div key={item.empId + key.id}>
<input defaultValue = {item.firstName} required/>
</div>
)
})}
</div>
)
} )
I suggest to choose open source UI library such as Material UI or Antd to display data inside table. It saves time to manage responsive web page styles.
I have initialized an empty return object that fixes all the problems and it worked like a charm. please check the below details.
class UserComponent extends React.Component {
constructor(props){
super(props)
this.state=({
users : [{
"id":"",
"depId":"",
"employees":[{
"empId":"",
"firstName":"",
"lastName":"",
"address":[{
"street":"",
"pin":""
}]
}]
}]
});
retrieve(e){
e.preventDefault();
fetch(`http://localhost:8080/test/{this.state.id}`,{
"method":"GET",
"headers":{
"accept":"application/json"
}
})
.then(response => response.json())
.then(response => {
this.setState({
users:response
})
}
render(){
return (
<div>
<form>
<table>
<tbody>
{
this.state.users === '' || null ? '' : Object.entries(this.state.users).map([key,value])=>(
Object.entries(key).map(field=>(
<tr>
<td>
<input name = "firstName" id = firstName" defaultValue = {field.firstName} required/>
</td>
</tr>
))))
}
</tbody>
</table>
</form>
</div>
)
}
}
export default UserComponent
I have a a JSON file which I would like to use its content into my React App.
This is an excerpt from my code
export default class App extends Component{
constructor(props){
super(props);
this.state = {
entry : []
}
}
componentDidMount(){
fetch(process.env.PUBLIC_URL + `./js/data.json`)
.then(res => res.json())
.then(json => this.setState({ entry: json }));
}
render(){
return(
<div>
<ul>
{this.state.entry.map( x => (
<li>
<div className='centering'>
<span>
<img alt='' key={x.img} src={process.env.PUBLIC_URL + `./img/${x.img}.jpg`}/>
</span>
<span className='txt'>
{ x.date }
</span>
<span>
<p>{ x.ctx }</p>
</span>
</div>
<div className='hr-line'></div>
</li>
))}
</ul>
</div>
)
}
}
this is the content of my data.json
{
"entry" : {
"2" : [
{
"date":"1/1/1",
"img":"profile",
"ctx":"as"
}
],
"1" : [
{
"date":"1/1/1",
"img":"profile",
"ctx":"as"
}
]
}
When I save the file, on the browser it shows TypeError: this.state is null
onrender {this.state.entry.map (x => ...
Is there something missing, or it is impossible to do so?
Thank you in advance.
Rendering is done before componentDidmount. You need to wait for your data to load before to do your map. Something like :
export default class App extends Component{
constructor(props){
super(props);
this.state = {
entry : [],
anyData: false
}
}
componentDidMount(){
fetch(process.env.PUBLIC_URL + `./js/data.json`)
.then(res => res.json())
.then(json => this.setState( prevState => ({ ...prevState, entry: json, anyData: true })));
}
render(){
if(!anyData) {
return <Loader />
}
return(
<div>
<ul>
{this.state.entry.map( x => (...))}
</ul>
</div>
)
}
}
There is also a possibility to use async lifecycle method :
async componentDidMount() {
const res = await fetch(...);
const json = await res.json();
this.setState( prevState => ({ ...prevState, entry: json, anyData: true }));
}
But it does not change the fact that it will be done after rendering.
React expects the .map for iterative rendering to work on an array of items only.
Therefore, changing the json input to return an array of items will help resolve the problem.
Existing input that does not work with .map:
"entry" : { "1" : [....], "2" : [....] } --> Input is not an array of items.
--> .map does not support this format
How to make it work with .map?
"entry" : [ {"1" : {....} , {"2" : {....} ] --> Input is an array of items.
--> .map works with this format
Additional: How to avoid 'null value' error in render method?
The render method must explicitly check for null condition before trying to render elements. This can be done as follows:
render(){
return(
<div>
<ul>
{ (this.state.entry) &&
this.state.entry.map( x => (
<li>
...
</li>
))
}
</ul>
</div>
)
}
I have a react app with a list of Div elements to create some Cards. Each card has 'read more' button to expand and collapse a paragraph and I toggle it for each mouse click. My problem is, for each click, it expand paragraphs in all cards instead only paragraph in the card I clicked. So I can't identify the clicked (this) card.
Component:
class BidCard extends Component {
constructor(props) {
super(props);
this.state = {
readMoreOpen: false,
}
}
readMore() {
this.setState({ readMoreOpen: !this.state.readMoreOpen })
}
render() {
const { articles } = this.props;
return (
articles.map(article => {
return (
<div className="projectCardRoot" key={article.id}>
<div className="projectCardMainLogin">
<div className="projectCardMiddle">
<p className={this.state.readMoreOpen ? 'openFullParagraph' : 'closeFullParagraph'} id="projectCardDesc">{article.description}</p>
<div className="cardReadMore desktopDiv" onClick={this.readMore.bind(this)}>Read more</div>
</div>
</div>
</div>
)
})
)
}
}
export default BidCard;
How can I solve this?
You can save id of the expanded card to the state and the check it when rendering items:
class BidCard extends Component {
constructor(props) {
super(props);
this.state = {
readMoreOpen: [], // Use array here
}
}
// Add card id to the expanded array if not already there, otherwise remove it
readMore = (id) => {
this.setState(state => {
if (state.readMoreOpen.includes(id)) {
return {readMoreOpen: state.readMoreOpen.filter(readId => readId !== id)}
}
return {readMoreOpen: [...state.readMoreOpen, id]}
})
}
render() {
const { articles } = this.props;
return (
articles.map(article => {
return (
<div className="projectCardRoot" key={article.id}>
<div className="projectCardMainLogin">
<div className="projectCardMiddle">
{/*Check if the item is in expanded items array */}
<p className={this.state.readMoreOpen.includes(article.id) ? 'openFullParagraph' : 'closeFullParagraph'} id="projectCardDesc">{article.description}</p>
<div className="cardReadMore desktopDiv" onClick={() => this.readMore(article.id)}>Read more</div>
</div>
</div>
</div>
)
})
)
}
}
You will need to keep expanded state per every card.
I would recommend to create component for card
articles.map(article => {
return (
<Article key={article.id} {...article} />
)
})
)
class Article extends Component {
state = {
readMoreOpen: false
}
readMore() {
this.setState(state => ({ readMoreOpen: !state.readMoreOpen }))
}
render () {
const {description} = this.props;
return (<div className="projectCardRoot" >
<div className="projectCardMainLogin">
<div className="projectCardMiddle">
<p className={this.state.readMoreOpen ? 'openFullParagraph' : 'closeFullParagraph'} id="projectCardDesc">{description}</p>
<div className="cardReadMore desktopDiv" onClick={this.readMore.bind(this)}>Read more</div>
</div>
</div>
</div>)
}
}
Other approach is to keep array of booleans with information of which article div should be currently expanded in this method you will need to update state with id of expanded article
readMore(id) {
this.setState({ articles: this.props.articles.map(article => article.id === id ? true : false) } )
}
and in render use boolean from state as information if it should be expanded
That's because all your cards currently share the same source of truth. You used a ternary operator to determine what class a Card would have depending on the state-value. Well, all Cards are using the same state-value to compare, so understandably, if one is affected, then all would be too.
There's more than one way to resolve this, but the most appropriate would probably be to create a separate Card Component. This makes it so each Card component has their own state to keep track of.
See working sandbox: https://codesandbox.io/s/quizzical-mahavira-wz8iu
Parent.js
import React from "react";
import ReactDOM from "react-dom";
import Card from "./Card";
import "./styles.css";
class BidCard extends React.Component {
render() {
const { articles } = this.props;
return articles.map(article => {
return <Card article={article} />;
});
}
}
BidCard.defaultProps = {
articles: [{ description: "woof" }, { description: "meow" }]
};
const rootElement = document.getElementById("root");
ReactDOM.render(<BidCard />, rootElement);
Card.js
import React, { useState } from "react";
const Card = ({ article }) => {
const [readOpen, setReadOpen] = useState(false);
return (
<div className="projectCardRoot" key={article.id}>
<div className="projectCardMainLogin">
<div className="projectCardMiddle">
<p
className={readOpen ? "openFullParagraph" : "closeFullParagraph"}
id="projectCardDesc"
>
{article.description}
</p>
<div
className="cardReadMore desktopDiv"
onClick={() => setReadOpen(!readOpen)}
>
Read more
</div>
</div>
</div>
</div>
);
};
export default Card;
I did a few modifications to your code. This way it should work.
I added comments that explain the the changes. The main idea is that you should not simply store the boolean readMoreOpen status (which in your code is treated as a kind of shared between all the cards) but specific card identity.
My changes works if there could be only one "expanded" card at any moment. If your design supposes that there could be a few "expanded" cards at the same time the solution would be more complex though not much.
class BidCard extends Component {
constructor(props) {
super(props);
// the way you've tried to keep status (open/closed) it wasn't tied to any speciifc card
// you should store this specific card instead
this.state = {
//readMoreOpen: false,
expandedCard: null,
}
this.readMore = this.readMore.bind(this);
}
readMore(article) {
//this.setState({ readMoreOpen: !this.state.readMoreOpen })
this.setState({expandedCard: article})
}
render() {
const { articles } = this.props;
const { expandedCard } = this.state;
return (
articles.map(article => {
// the look of each card depends on state.expandedCard only if article == expandedCard it's shown with 'openFullParagraph' class
return (
<div className="projectCardRoot" key={article.id}>
<div className="projectCardMainLogin">
<div className="projectCardMiddle">
<p className={article == expandedCard ? 'openFullParagraph' : 'closeFullParagraph'} id="projectCardDesc">{article.description}</p>
<div className="cardReadMore desktopDiv" onClick={() => this.readMore(article)}>Read more</div>
</div>
</div>
</div>
)
})
)
}
}
export default BidCard;
I have a react component in which user can upload Image and he's also shown the preview of uploaded image. He can delete the image by clicking delete button corresponding to Image. I am using react-dropzone for it. Here's the code:
class UploadImage extends React.Component {
constructor(props) {
super(props);
this.onDrop = this.onDrop.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.state = {
filesToBeSent: [],
filesPreview: [],
printCount: 10,
};
}
onDrop(acceptedFiles, rejectedFiles) {
const filesToBeSent = this.state.filesToBeSent;
if (filesToBeSent.length < this.state.printCount) {
this.setState(prevState => ({
filesToBeSent: prevState.filesToBeSent.concat([{acceptedFiles}])
}));
console.log(filesToBeSent.length);
for (var i in filesToBeSent) {
console.log(filesToBeSent[i]);
this.setState(prevState => ({
filesPreview: prevState.filesPreview.concat([
<div>
<img src={filesToBeSent[i][0]}/>
<Button variant="fab" aria-label="Delete" onClick={(e) => this.deleteImage(e,i)}>
<DeleteIcon/>
</Button>
</div>
])
}));
}
} else {
alert("you have reached the limit of printing at a time")
}
}
deleteImage(e, id) {
console.log(id);
e.preventDefault();
this.setState({filesToBeSent: this.state.filesToBeSent.filter(function(fid) {
return fid !== id
})});
}
render() {
return(
<div>
<Dropzone onDrop={(files) => this.onDrop(files)}>
<div>
Upload your Property Images Here
</div>
</Dropzone>
{this.state.filesToBeSent.length > 0 ? (
<div>
<h2>
Uploading{this.state.filesToBeSent.length} files...
</h2>
</div>
) : null}
<div>
Files to be printed are: {this.state.filesPreview}
</div>
</div>
)
}
}
export default UploadImage;
My Question is my component is not re-rendering even after adding or removing an Image. Also, I've taken care of not mutating state arrays directly. Somebody, please help.
Try like this, I have used ES6
.
Need help with react...
Trying to implement a collapsible list of cards with weather information.
Already implemented the behavior of expand and collapse, but when i clicked on one panel the other panel open at the same time (i have 2 panels and need 7 to display weahter for 7 days of the week).
How can i open and close just one panel?
Code:
import React, { Component } from 'react';
import Moment from 'react-moment';
import RandomGif from './RandomGif.js';
const urlForCity = city => `https://cors-anywhere.herokuapp.com/http://api.openweathermap.org/data/2.5/forecast/daily?q=${city}&units=metric&cnt=7&appid=1fba7c3eaa869008374898c6a606fe3e`
class OpenWapi extends Component {
constructor(props) {
super(props);
this.state = {
requestFailed: false,
shown: false
}
this.componentDidMount = this.componentDidMount.bind(this);
this.toggle = this.toggle.bind(this);
}
componentDidMount() {
fetch(urlForCity(this.props.city))
.then(response => {
if(!response.ok) {
throw Error("Network request failed")
}
return response;
})
.then(data => data.json())
.then(data => {
this.setState({
weatherData: data
})
}, () => {
this.setState({
requestFailed: true
})
})
}
toggle() {
this.setState({
shown: !this.state.shown
});
}
render() {
if(this.state.requestFailed) return <p>Request Failed.</p>;
if(!this.state.weatherData) return <p>Loading...</p>;
return (
<div>
<p>City: {this.state.weatherData.city.name}</p>
{/* Day 1 */}
<div onClick={this.toggle} className="dayWeekItem">
<div className="top-content">
<div className="icon-weather"></div>
<div className="date">
<div className="weekday">Today</div>
<div className="day-long"><Moment unix format="MMM DD YYYY">{this.state.weatherData.list[0].dt}</Moment></div>
</div>
<div className="temperature">
<div className="temp-high">{parseInt(this.state.weatherData.list[0].temp.max)}º</div>
<div className="temp-low">{parseInt(this.state.weatherData.list[0].temp.min)}º</div>
</div>
</div>
<div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.state.weatherData.list[0].weather[0].description} />
</div>
</div>
</div>
{/* Day 2 */}
<div onClick={this.toggle} className="dayWeekItem">
<div className="top-content">
<div className="icon-weather"></div>
<div className="date">
<div className="weekday">Tomorrow</div>
<div className="day-long"><Moment unix format="MMM DD YYYY">{this.state.weatherData.list[1].dt}</Moment></div>
</div>
<div className="temperature">
<div className="temp-high">{parseInt(this.state.weatherData.list[1].temp.max)}º</div>
<div className="temp-low">{parseInt(this.state.weatherData.list[1].temp.min)}º</div>
</div>
</div>
<div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.state.weatherData.list[1].weather[0].description} />
</div>
</div>
</div>
{/* Day 3 */}
{/* Day 4 */}
{/* Day 5 */}
</div>
)
}
}
export default OpenWapi;
I would have an object to represent the state, a field for each panel.
Like this:
constructor(props) {
...
this.state = {
requestFailed: false,
shown: {}
}
...
}
...
toggle(panelNumber) {
this.setState({
shown: {
...this.state.shown,
[panelNumber]: !this.state.shown[panelNumber]
}
});
}
...
The toogle function is used like this, for instance, Day 1:
<div onClick={() => this.toggle(1)} className="dayWeekItem">
...
</div>
And to show in html, for instance, Day 1:
<div className={this.state.shown[1] ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.state.weatherData.list[0].weather[0].description} />
</div>
</div>
They all will collapse always with your implementation.
You have a state
state = {
shown: true
}
You have a function to toggle it
toggle = () => {
this.setState(shown: !this.state.shown)
}
And you render the component, using the this.state.shown in two places, but the value will always be one true or false
render() {
return(<div .....//something>
<div onClick={this.toggle}>
{ this.state.shown ? <SomeComponent or HTML Tag> : null }
</div>
<div onClick={this.toggle}>
{ this.state.shown ? <SomeComponent or HTML Tag> : null }
</div>
</div>)
}
So where ever you toggle, once the state is updated and render method is called again to paint the view, both sections of divs get the sameBoolean` value. Therefore, they both collapse.
Best Solution I can offer for this problem will be:
Create a separate component which has two jobs to be do:
1. Maintains its own state, of collapse true or false.
2. Render the children given to it without wondering what they might be.
So let say
class WeatherWidget extends React.PureComponent {
state= {
shown: true
}
toggle = () => this.setState({shown: !this.state.shown})
render() {
return(
<div onClick={this.toggle} className="dayWeekItem">
<div className="top-content">
<div className="icon-weather"></div>
<div className="date">
<div className="weekday">Today</div>
<div className="day-long">
<Moment unix format="MMM DD YYYY">{this.props.date}</Moment>
</div>
</div>
<div className="temperature">
<div className="temp-high">{parseInt(this.props.maxTemp)}º
</div>
<div className="temp-low">{parseInt(this.props.minTemp)}º
</div>
</div>
</div>
<div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
<div className="weather-gif" >
<RandomGif keyword={this.props.gifDescription} />
</div>
</div>
</div>
)
}
}
So you create a reusable component which manages its own state ( React Paradigm/ Composition brings reusability)
As for displaying multiple widgets
class OpenWapi extends Component {
constructor(props) {
super(props);
this.state = {
requestFailed: false,
shown: false
}
this.componentDidMount = this.componentDidMount.bind(this);
this.toggle = this.toggle.bind(this);
}
componentDidMount() {
fetch(urlForCity(this.props.city))
.then(response => {
if(!response.ok) {
throw Error("Network request failed")
}
return response;
})
.then(data => data.json())
.then(data => {
this.setState({
weatherData: data
})
}, () => {
this.setState({
requestFailed: true
})
})
}
render() {
if(this.state.requestFailed) return <p>Request Failed.</p>;
if(!this.state.weatherData) return <p>Loading...</p>;
return(
<div>
<p>City: {this.state.weatherData.city.name}</p>
<WeatherWidget
date={this.state.weatherData.list[0].dt}
maxTemp={this.state.weatherData.list[0].temp.max}
minTemp={this.state.weatherData.list[0].temp.min}
gifDescription=
{this.state.weatherData.list[0].weather[1].description}
/>
<WeatherWidget
date={this.state.weatherData.list[1].dt}
maxTemp={this.state.weatherData.list[1].temp.max}
minTemp={this.state.weatherData.list[1].temp.min}
gifDescription=
{this.state.weatherData.list[1].weather[1].description}
/>
</div>
)
}
Hopefully, this solves the use case.