Conditional rendering element from a list as a modal in React - reactjs

I am very new to programming and working on an MVP project for a FS app where I have a small database of books which I render through a .map. Currently, I am rendering the cover of all the books in my database and when I click on the cover it shows the title, author and summary. The functionality is working but it is looking rubbish as when I click any book it pushes everything around and I would like for it to show as a modal box above my list of books. This is my code below, do you have any idea how to achieve that? Thanks so much :)
import Filter from "./components/filter"
import './App.css';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
books:[],
showBook: []
};
}
componentDidMount() {
this.getBookclub();
}
getBookclub = () => {
fetch(`/books`)
.then(response => response.json())
.then(response => {
this.setState({ books: response });
});
};
handleClick(e){
for (let i = 0 ; i < this.state.books.length ; i++) {
this.state.showBook[i] = false;
}
let bookShow = [...this.state.showBook];
bookShow[e.target.name-1] = true;
this.setState({
showBook: bookShow
});
}
renderLibrary() {
return this.state.books.map((books,id) => {
return(
<li key={id} className="book-list">
<span onClick ={() => this.getBookclub(books.id)}>
<div>
**<img onClick={(e) => this.handleClick(e)} name={books.id} src={books.cover} alt={books.title}/>
</div>
<div className={this.state.showBook[books.id-1] ? "bookDetails" : "bookhidden"}>
<br/>
<div className="cover-book-show">
<h5>{books.title}</h5>
</div>
<div className="author-book-show">
<h6>{books.author}</h6>
</div>
<div className="summary-book-show">
<p>{books.summary}</p>
</div>**
</div>
</span>
</li>
)
})}
filterBook(filteredList){
this.setState({
books: filteredList
})
}
render() {
return (
<div>
<h1>Books</h1>
<div>
<Filter filterBook={filteredList => this.filterBook(filteredList)}/>
</div>
<br/>
<ul>
**<div className="all-books">
{this.renderLibrary()}
</div>**
</ul>
</div>
);
}
}```

Related

React app not showing in Codepen no matter what?

I have a react app that I made in VS Studio, putting it into codepen, it doesnt seem to load a thing, any suggestions?
I have tried making sure React is linked and checked all of my syntax, no errors on local host but no display in codepen.
I have looked through the code multiple times and I feel its such a silly mistake
https://codepen.io/donnieberry97/pen/EzmOvW
class TodoListt extends React.Component {
state = {};
constructor(props) {
super(props);
this.state = {
userInput: "",
list: [],
editing: false,
};
}
changeUserInput(input) {
this.setState({
userInput: input
})
}
addToList() {
if (this.state.userInput === "") { (alert("Please enter a To-do")); return; };
const { list, userInput } = this.state;
this.setState({
list: [...list, {
text: userInput, key: Date.now(), done: false
}],
userInput: ''
})
}
handleChecked(e, index) {
console.log(e.target.checked);
const list = [...this.state.list];
list[index] = { ...list[index] };
list[index].done = e.target.checked;
this.setState({
list
})
}
handleEditing(e) {
this.setState({
editing: true
})
}
handleRemoved(index) {
const list = [...this.state.list];
list.splice(index, 1);
this.setState({
list
})
}
render() {
var viewStyle = {};
var editStyle = {};
if (this.state.editing) {
viewStyle.display = "none"
}
else {
editStyle.display = "none"
}
return (
<div className="to-do-list-main">
<input
onChange={(e) => this.changeUserInput(e.target.value)}
value={this.state.userInput}
type="text"
/>
<div class="submitButton">
<button onClick={() => { this.addToList(this.state.userInput) }}>Add todo</button>
</div>
{this.state.list.map((list, index) => (
<div className="form">
<ul>
{/* <div style={viewStyle} onDoubleClick={this.handleEditing.bind(t his)}> */}
<li key={list.key}>
<div class="liFlexCheck">
<input type="checkbox" onChange={(e) => this.handleChecked(e, index)} />
</div>
<div class="liFlexText">
<div class="liFlexTextContainer">
<span style={{ textDecoration: list.done ? 'line-through' : 'inherit' }}>
{list.text}
</span>
</div>
</div>
<button onClick={(index) => this.handleRemoved(index)}>Remove</button>
<input
type="text"
style={editStyle}
value={list.text}
/>
</li>
{/* </div> */}
</ul>
</div>
))}
</div>
);
}
}
Remove the import statements, working example.
You shouldn't use import when you got External Scripts.
Also, you got many errors in your code that should be handled, like:
<div class="submitButton">, use className.
Each child in a list should have a unique key prop.
Form field with value prop but without onChange handler.
Check out the logs:
In codpen, you don't need to import the react instead just write code,
here is codepen working one : codepen
from codesandbox, you can learn with all imports also because it doesn't uses any external scripts,
your code will work fine if you add an import to it
that is import ReactDOM from 'react-dom';
codesandbox will show all these suggestions,
here is codesandbox working example: codesandbox

How to print list output dynamically in React JS as buttons

Below is the code snippet of my ReactJS component, I'm trying to put the output with a button beside it in a list format. The output which is received in the "axios" get function is from an api and it is in this format: [{'name': 'i-cc608f4d'}, {'name': 'i-fd608f7c'}, {'name': 'i-fe608f7f'}]
I want to list these IDs with a button besides it.
import React, { Component } from "react";
import axios from "axios";
const pStyle = {
backgroundColor: "#79D3EF",
color: "white"
};
export default class CityContent extends Component {
constructor(props) {
super(props);
this.state = {
citydetail: []
};
}
componentDidMount() {
axios.get("http://127.0.0.1:8000/Delhi").then(res => {
const citydetail = res.data;
this.setState({ citydetail });
});
}
render() {
return (
<div className="content-wrapper">
<section className="content-header">
<div className="row">
<div className="col-md-4">
<div className="box">
<div className="box-body">
<div className="row">
<div className="col-md-8">
<ul>{this.state.citydetail.state}</ul>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
);
}
What should I write to replace {this.state.citydetail.state}.
Because currently, it's printing the API output itself.
<div className="col-md-8">
<ul>
{
this.state.citydetail.map((city) => (
<li>{city.name}<button>Click</button></li>
)}
</ul>
</div>
This code will print your output and map will map through that data of citydetail and button beside it.
It should be something like this
<ul>
{
this.state.citydetail.map((city, index) => (
<li key={index}>{city.name}<button>{city.name}</button></li>
)}
</ul>
Update
Set your state like this in componentDidMount()
this.setState({ citydetail : citydetail });
Update 2 (after OP comment)
I cannot change the format of this output...
try this
axios.get("http://127.0.0.1:8000/Delhi").then(res => {
const citydetail = res.data;
let arrData = JSON.parse(citydetail.replace(/'/g, '"'));
this.setState({ citydetail: arrData });
});

Is there a way to map over the cities on my cities array using the api and rendering the info from all cities on the array?

Right now is only displaying the info for the first item.
I stored the cities I want to get info from in the constant and now I
I am trying to get the info from each to display.
I am not sure how to go about it.
class HomePage extends Component {
state = {
weatherResults: []
};
componentDidMount() {
const cities = ["Boston", "New York"];
fetch(`http://api.openweathermap.org/data/2.5/forecast?id=52490&units=imperial&appid=${API_KEY}&q=${cities}&cnt=60`)
.then(res => res.json())
.then(results => {
this.setState({
weatherResults: results
});
console.log(results);
})
.catch(error => console.error(error));
}
render() {
const { weatherResults } = this.state;
return (
<div>
{this.state.weatherResults.length !== 0 && (
<div className="container" key={weatherResults.city.id}>
<h2> {weatherResults.city.name} </h2>
<p> {weatherResults.list[0].main.temp}</p>
<p>{weatherResults.list[0].weather[0].description}</p>
<p>
Humidity:
{weatherResults.list[0].main.humidity}
</p>
<p> Wind: {weatherResults.list[0].wind.speed} </p>
</div>
)}
</div>
);
}
}
export default HomePage;
You could create a separate fetch request for each city and use Promise.all to put the result of both requests in the state when both requests have finished.
You can then use map on the weatherResults array to display the information about both cities in the render method.
Example
class HomePage extends Component {
state = {
weatherResults: []
};
componentDidMount() {
const cities = ["Boston", "New York"];
const promises = cities.map(city => {
return fetch(`http://api.openweathermap.org/data/2.5/forecast?id=52490&units=imperial&appid=${API_KEY}&q=${city}&cnt=60`)
.then(res => res.json());
});
Promise.all(promises)
.then(weatherResults => {
this.setState({ weatherResults });
})
.catch(error => console.error(error));
}
render() {
const { weatherResults } = this.state;
if (weatherResults.length === 0) {
return null;
}
return (
<div className="container">
{weatherResults.map(weatherResult => (
<div key={weatherResult.city.id}>
<h2>{weatherResult.city.name}</h2>
<p>{weatherResult.list[0].main.temp}</p>
<p>{weatherResult.list[0].weather[0].description}</p>
<p>Humidity: {weatherResult.list[0].main.humidity}</p>
<p>Wind: {weatherResult.list[0].wind.speed}</p>
</div>
))}
</div>
);
}
}
you can do something like this.
render() {
const { weatherResults } = this.state;
return (
<div>
{ this.state.weatherResults.length &&
<div className = "container">
<h2> { weatherResults.city.name} </h2>
</div>
}
{
this.state.weatherResults.list.map((ele, idx) => (
<div>
<p> {ele.main.temp}</p>
<p>
{ele.weather[0].description}
</p>
<p> Humidity:
{ele.main.humidity} </p>
<p> Wind: {ele.wind.speed} </p>
</div>
))
}
</div>
);
}
}
Essentially what I'm doing above is creating an array of react components and displaying them based on content in your list. Now I'm not 100% sure what your JSON structure looks like so i just made assumptions based on the code you posted above.
If possible id move all the content related to the JSON into the map function.
This portion:
{ this.state.weatherResults.length &&
<div className = "container">
<h2> { weatherResults.city.name} </h2>
</div>
}
Also it is recommended that each element in the array/map call have its own unique key that is not the index. so if the JSON contains some unique identifier like a primary key from a data base use it.
To render items from Array in React you should use Array.prototype.map().
For example:
render() {
const { weatherResults } = this.state;
return (
<div>
{
weatherResults.list.length > 0 &&
weatherResults.list.map(item => (
<div className="container" key={weatherResults.city.id}>
<h2> {item.city.name} </h2>
<p> {item.main.temp}</p>
<p>{item.weather[0].description}</p>
<p>Humidity: {item.main.humidity}</p>
<p> Wind: {item.wind.speed} </p>
</div>
));
}
</div>
);
}

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

React: change order list when button clicked

I am making my first app with Javascript and React and started with a page which views a shopping list. It gets the items from an api call.
If the user clicks on the button 'done' (or should I use an checkbox?) This product should go to the bottom of the list (and be grayed out with css but thats not the problem).
The problem is, I have no clue how to do this. Can anyone help me out a bit?
This is my code:
import React from 'react';
//import image from '../images/header.png';
//import Collapsible from './Collapsible';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
orders: []
}
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/15-03-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
componentWillUpdate(nextProps, nextState) {
localStorage.setItem('orders', JSON.stringify(nextState.orders));
localStorage.setItem('ordersDate', Date.now());
}
render() {
const {isLoading, orders} = this.state;
return (
<div>
<header>
<img src="/images/header.jpg"/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map(order => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv">
<img className="plaatje" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
<p>ID: {orderid}</p>
</div>
<div className="bdone">
<button className="btn btn-sm btn-default btndone">Done</button>
</div>
</div>
}) : null
}
</div>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
}
export default App;
You can achieve by using this :
this.handleDoneAction = event = > {
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
var itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
var updatedOrderList = prevOrders.concat(itemToMoveAtLast);
this.setState({order: updatedOrderList})
}
I have attach an event handler on the button handleDoneAction.
<button className="btn btn-sm btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
the attribute data-itemIndex is the index of the object in orders array.
And your map function will be like this:
orders.map((order, index) => {
//content
})
ANd for the different style effects on the done products, I will suggest you to use different array for all done products.

Resources