crud: How to make name and area change - javascript

I'm using React trying to make a full crud application, but I can't figure out how to make the name and area for the rooms created in the houses change. Does anyone have any suggestions or know how to make this work?
Also sorry for all the code idk what is needed for people to see.
App.js
import React, { Component } from 'react';
import { HousesList } from './Components/Houses-List'
// import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div>
<HousesList/>
</div>
)
}
}
export default App;
House.js
import React from 'react';
import { NewRoomForm } from './NewRoomForm';
import { EditRoomForm } from './EditRoomForm';
export const House = (props) => {
const { house, updateHouse } = props;
const deleteRoom = (roomId) => {
const updatedHouse = {
...house,
rooms: house.rooms.filter((x) => x._id !== roomId)
};
updateHouse(updatedHouse);
}
const addNewRoom = (room) => updateHouse({ ...house, rooms: [...house.rooms, room]});
const rooms = () => (
<ul>
{house.rooms.map((room, index) => (
<li key={index}>
<label> {`${room.name} Area: ${room.area}`} </label>
<button onClick={(e) => deleteRoom(room._id)}>Delete Room</button>
<EditRoomForm />
<h1> </h1>
</li>
))}
</ul>
);
return (
<div>
<h1>{house.name}</h1>
{
rooms({ rooms, houseId: house._id, deleteRoom})
}
<NewRoomForm addNewRoom={addNewRoom} />
{/* <EditRoomForm addNewRoom={addNewRoom} /> */}
</div>
)
};
EditRoomForm.js
import React, { useState } from 'react';
export const EditRoomForm = (props) => {
const [name, setName] = useState('');
const [area, setArea] = useState('');
const handleAreaInput = (e) => {
const int = parseInt(e.target.value, 10);
setArea(int >= 0 ? int : '');
}
const onSubmit = (e) => {
e.preventDefault();
if (name && area) {
console.log("name: " + name)
console.log("area: " + area)
setName('');
setArea('');
console.log("name: " + name)
console.log("area: " + area)
} else {
console.log('invalid input');
}
};
return (
<div>
<form onSubmit={onSubmit}>
<input
type='text'
placeholder='name'
onChange={(e) => setName(e.target.value)}
value={name}
/>
<input
type='text'
placeholder='area'
onChange={handleAreaInput}
value={area}
/>
<button type='submit'>Edit Room</button>
</form>
</div>
)
};
NewRoomForm.js
import React, { useState } from 'react';
export const NewRoomForm = (props) => {
const [name, setName] = useState('');
const [area, setArea] = useState('');
const handleAreaInput = (e) => {
const int = parseInt(e.target.value, 10);
setArea(int >= 0 ? int : '');
}
const onSubmit = (e) => {
e.preventDefault();
if (name && area) {
props.addNewRoom({name, area});
setName('');
setArea('');
} else {
console.log('invalid input');
}
};
return (
<div>
<h4>Add a new room</h4>
<form onSubmit={onSubmit}>
<input
type='text'
placeholder='name'
onChange={(e) => setName(e.target.value)}
value={name}
/>
<input
type='text'
placeholder='area'
onChange={handleAreaInput}
value={area}
/>
<button type='submit'>Add/Edit Room</button>
</form>
</div>
)
};
Houses-List.js
import React from 'react';
import { House } from './House';
import { housesApi } from '../rest/HousesApi';
import { NewRoomForm } from './NewRoomForm';
// import { EditRoomForm } from './edit';
export class HousesList extends React.Component {
state = {
houses: []
};
componentDidMount() {
console.log("Inside componentDidMount Funcaton Block")
this.fetchHouses();
}
fetchHouses = async () => {
console.log("Inside fetchHouses Funcaton Block")
const houses = await housesApi.get();
this.setState({ houses });
}
updateHouse = async (updatedHouse) => {
console.log("Inside updateHouse Funcaton Block")
console.log("housesApi Check: " + housesApi)
await housesApi.put(updatedHouse);
this.fetchHouses();
};
render() {
console.log("Inside render of Houses-List")
console.log(this.state.houses);
return (
<div className="house-list">
{this.state.houses.map((house) => (
<House
house={house}
key={house._id}
updateHouse={this.updateHouse}
/>
))}
<NewRoomForm />
</div>
)
}
}
HousesApi.js
const HOUSES_ENDPOINT = "https://ancient-taiga-31359.herokuapp.com/api/houses";
class HousesApi {
get = async () => {
try {
console.log("Inside Try Block Of HousesAPI get funcation")
const resp = await fetch(HOUSES_ENDPOINT);
const data = await resp.json();
return data;
} catch(e) {
console.log("Oops, looks like fetchHouses has an issue", e)
}
}
put = async (house) => {
try {
console.log("Inside Try Block Of put funcation")
console.log(house)
console.log("HOUSES_ENDPOINT: " + HOUSES_ENDPOINT + house._id)
console.log("house._id: " + house._id)
const resp = await fetch(`${HOUSES_ENDPOINT}/${house._id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(house)
});
console.log("The body is: " + JSON.stringify(house))
// console.log(" resp.json(): " + resp.json())
console.log("At The End OF Try Block Of put funcation")
return await resp.json();
} catch(e) {
console.log("Oops, looks like updating houses has an issue", e)
}
}
}
export const housesApi = new HousesApi();

Before helping on the code, I will explain the concept on updating the rooms based on your code structure.
The EditRoomForm needs to rendered with selected room props and curried function with index parameter.
The EditRoomForm has its own state of room which will be updated when there is an incoming selected room props by using useEffect.
User updates the name and area and click submit which will trigger a callback function props which will send back the new name and area and update those new name and area by using index inside the house.rooms.
Parent House.js will call the house API PUT method with updated details.
With the steps above, you will need to update your code like this.
House.js
import React from 'react';
import { NewRoomForm } from './NewRoomForm';
import { EditRoomForm } from './EditRoomForm';
export const House = (props) => {
const { house, updateHouse } = props;
const deleteRoom = (roomId) => {
const updatedHouse = {
...house,
rooms: house.rooms.filter((x) => x._id !== roomId)
};
updateHouse(updatedHouse);
}
const updateRoom = (index) => (name,area) => {
const updatingHouse = JSON.parse(JSON.stringify(house));
updatingHouse.rooms[index] = {...updatingHouse.rooms[index], name,area}
updateHouse(updatingHouse );
}
const addNewRoom = (room) => updateHouse({ ...house, rooms: [...house.rooms, room]});
const rooms = () => (
<ul>
{house.rooms.map((room, index) => (
<li key={index}>
<label> {`${room.name} Area: ${room.area}`} </label>
<button onClick={(e) => deleteRoom(room._id)}>Delete Room</button>
<EditRoomForm selectedRoom={room} onSubmit={updateRoom(index)} />
<h1> </h1>
</li>
))}
</ul>
);
return (
<div>
<h1>{house.name}</h1>
{
rooms({ rooms, houseId: house._id, deleteRoom})
}
<NewRoomForm addNewRoom={addNewRoom} />
</div>
)
};
EditRoomForm.js
import React, { useState,useEffect } from 'react';
export const EditRoomForm = (props) => {
const [name, setName] = useState('');
const [area, setArea] = useState('');
const {selectedRoom} = props;
useEffect(() => {
setName(selectedRoom.name);
setArea(selectedRoom.area);
}, [selectedRoom] )
const handleAreaInput = (e) => {
const int = parseInt(e.target.value, 10);
setArea(int >= 0 ? int : '');
}
const onSubmit = (e) => {
e.preventDefault();
if (name && area) {
console.log("name: " + name)
console.log("area: " + area)
props.onSubmit(name,area);
setName('');
setArea('');
console.log("name: " + name)
console.log("area: " + area)
} else {
console.log('invalid input');
}
};
return (
<div>
<form onSubmit={onSubmit}>
<input
type='text'
placeholder='name'
onChange={(e) => setName(e.target.value)}
value={name}
/>
<input
type='text'
placeholder='area'
onChange={handleAreaInput}
value={area}
/>
<button type='submit'>Edit Room</button>
</form>
</div>
)
};
You get the idea.

You can add a button and use useState hook to keep the state. On button click set the editingRoom state to true. On state change the component will be re-rendered and EditForm component will be shown. You need to pass proper props. And once editing is completed please reset the editingRoom state to false.
import React, { useState } from 'react';
const [editingRoom, setEditingRoom] = useState(false);
const handleEditRoom = (room) => {
.......
setEditingRoom(false);
}
const rooms = () => (
<ul>
{house.rooms.map((room, index) => (
<li key={index}>
<label> {`${room.name} Area: ${room.area}`} </label>
<button onClick={(e) => deleteRoom(room._id)}>Delete Room</button>
{!editingRoom && <button onClick={(e) => seEditingRoom(true)}>Edit Room</button>}
{editingRoom && <EditRoomForm handleEditRoom={handleEditRoom} />}
<h1> </h1>
</li>
))}
</ul>

Related

How can I make each student add their own identifier tags?

I'm reading from an API and displaying each student's information onto the web browser. My goal is to make each student have a text input field in which you are able to add a 'tag' for the specific student.
My problem: Currently, I have given each student a text input field but when I enter a 'tag' for ANY of the students, it only adds the tag to the FIRST student.
Home.jsx
import axios from 'axios';
import { useState, useEffect } from 'react';
import Students from '../components/Students';
import styles from "./Home.module.css";
const Home = () => {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const fetchStudents = async () => {
const response = await axios.get(`https://api.hatchways.io/assessment/students`);
setStudents(response.data.students);
setFilteredStudents(response.data.students);
console.log(response.data.students);
}
const searchStudentName = async (searchName) => {
const searchNameFiltered = searchName.toLowerCase();
console.log(searchNameFiltered);
if (searchNameFiltered === "") {
fetchStudents();
return;
}
var newArray = await students.filter((student) => {
return student.firstName.toLowerCase().includes(searchNameFiltered)
|| student.lastName.toLowerCase().includes(searchNameFiltered);
})
await setFilteredStudents(newArray);
}
useEffect(() => {
fetchStudents();
}, [])
return(
<>
<div>
<input className={styles.nameSearchInput} type="text" placeholder="Search by name" onChange={(event) => searchStudentName(event.target.value) }/>
{filteredStudents.map((student) => (
<Students key={student.id} student={student} />
))}
</div>
</>
)
}
export default Home;
Students.jsx
import { useState } from 'react';
import styles from "../views/Home.module.css";
import { v4 as uuidv4 } from 'uuid';
import AddIcon from '#mui/icons-material/Add';
import RemoveIcon from '#mui/icons-material/Remove';
const Students = ({student}) => {
const [isShown, setIsShown] = useState(true);
const findAverageGrade = arr => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += parseInt(arr[i]);
}
return sum / arr.length;
}
const addTag = (event) => {
if (event.key === 'Enter') {
document.getElementById("tag-output").innerHTML += `
<p id="tag">${event.target.value}</p>
`;
document.getElementById("tag-input").value = "";
}
}
return (
<div key={student.email} className={styles.studentItem}>
<img className={styles.studentImage} src={student.pic} />
<div className={styles.studentInfoContainer}>
<div className={styles.studentHeader}>
<p className={styles.studentName}>{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}</p>
<button className={styles.expandBtn} onClick={() => {
setIsShown(!isShown);
}}>
{ isShown ? <AddIcon className={styles.expandBtn} /> : <RemoveIcon className={styles.expandBtn} /> }
</button>
</div>
<ul className={styles.studentDetail}>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>Average: {findAverageGrade(student.grades)}%</li>
{!isShown ? <div>
<table className={styles.gradesTable}>
<tbody>
{student.grades.map((grade) => (
<tr key={uuidv4()}>
<td>Test</td>
<td>{grade}%</td>
</tr>
))}
</tbody>
</table>
</div>
: null }
<div id="tag-output"></div>
<input id="tag-input" className={styles.addTagInput} type="text" placeholder="Add a tag" onKeyPress={(e) => addTag(e)}/>
</ul>
</div>
</div>
)
}
export default Students;
use array state and append it with new values like this
play live code here live_demo
import React, { useState } from "react";
export default function Main(props) {
const [tags, setTags] = useState([]);
return (
<div className="App">
<input
type="text"
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey && e.target.value !== "") {
let val = e.target.value;
e.target.value = "";
setTags([...tags, val]);
}
}}
/>
<div className="tags">
<ol className="each-tag">
{tags.map((tag, i) => {
return <li key={i}>{tag}</li>;
})}
</ol>
</div>
</div>
);
}

How to prevent my Page render when I click on the button

I am trying to optimize my react application, while profiling my application I found that when I click on Add to cart page my whole page is getting re-rendered. Could anyone help me with, how to avoid that and why it is happening?
FYR, GitHub repo:https://github.com/sandeep8080/shopping-cart-assignment
import { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import SideBar from "../../components/sideBar/SideBar";
import { getProductsData } from "../../redux/action/products";
import ProductCard from '../../components/productCard/ProductCard';
import './products.css';
import { getCategoryData } from "../../redux/action/category";
import Cart from "../cart/cart";
import Modal from '../../components/modal/Modal';
import { useHistory, useParams } from "react-router";
const ProductsPage = () => {
const dispatch = useDispatch();
const router = useHistory();
const { id } = useParams();
console.log(` product comp : ${id}`);
const productsData = useSelector(data => data.Products.products);
const sideBarData = useSelector(data => {
const listItems = data.Categories.CategoriesItems;
const activeListItems = listItems.filter(item => item.enabled === true);
return activeListItems;
});
const openCart = useSelector(state => state.CartDetails.isOpen);
const [fProductData, setFProductData] = useState([]);
useEffect(() => {
dispatch(getProductsData());
dispatch(getCategoryData());
}, []);
useEffect(() => {
if (id) {
filterDataByCategory(id);
} else {
setFProductData(productsData);
}
}, [productsData, id]);
// Function to filter out the data based on category
const filterDataByCategory = (id) => {
console.log("Filter data function called")
const filterData = productsData.filter(item => item.category === id);
setFProductData(filterData);
};
const handleClickProduct = useCallback((id) => {
filterDataByCategory(id);
router.push(`/products/${id}`);
}, [id]);
return (
<div className='product-main'>
<SideBar
sideBarData={sideBarData}
handleClickProduct={handleClickProduct}
/>
<div className='product-container'>
<div className='product-row'>
{
(fProductData).map((product) => {
return (
<div key={product.id} className='card-wrapper' >
<ProductCard key={product.id} {...product} />
</div>
)
})
}
</div>
</div>
{
openCart &&
<Modal>
<Cart />
</Modal>
}
</div >
)
};
export default ProductsPage;
// Product Card component
import './ProductCard.css';
import Button from '../button/Button';
import React from 'react';
import { useDispatch } from 'react-redux';
import { updateCart } from '../../redux/action/cart';
import priceFromatter from '../../lib/priceFromatter';
const ProductCard = ({ name, price, description, imageURL, id }) => {
const dispatch = useDispatch();
const handleClick = () => {
console.log('product clicked', id);
dispatch(updateCart(id, 'add'));
};
let imgURL = `../../${imageURL}`;
// imgURL = imgURL.replace(/([^:]\/)\/+/g, "$1");
// const image = React.lazy(() => import (`${imgURL}`));
// console.log(image);
return (
<article className='card-container'>
<h6 className='card-header'>
{name}
</h6>
<div className='content-container'>
<img
className='content-img'
// src={require(`${imgURL}`).default}
src={imageURL}
/>
<div className='content'>
<p className='content-desc'>{description}</p>
<div className='content-footer'>
<p>{priceFromatter(price)}</p>
<Button btnText='Add To Cart' handleClick={() => handleClick(id)} />
</div>
</div>
</div>
</article>
)
};
export default ProductCard;
import { callApi } from "../../lib/api";
import { actions } from '../actionContants/actionConstant';
export const toggleCart = (isToggle) => {
return {
type: actions.OPEN_CART,
payload: isToggle,
}
};
export const updateCart = (id, operation) => {
return async (dispatch, getState) => {
const productList = getState().Products.products;
const cartItems = getState().CartDetails.cartItems;
const currItem = productList.find(({ id: currentItemId }) => currentItemId === id);
const isItemInCart = cartItems.find(({ id }) => id === currItem.id);
let finalItem = [];
if (!isItemInCart) {
finalItem = [...cartItems, { ...currItem, count: 1 }]
} else {
finalItem = cartItems.map(item => {
if (item.id === currItem.id) {
operation === 'add' ? item.count = item.count + 1 : item.count = item.count - 1
}
return item;
}).filter(({ count }) => count)
}
try {
const result = await callApi.post('/addToCart', id);
result && dispatch({
type: actions.UPDATE_TO_CART,
payload: finalItem
})
} catch (error) {
console.log(error)
}
}
};
In products.js change the following block of code:
const sideBarData = useSelector(data => {
const listItems = data.Categories.CategoriesItems;
const activeListItems = listItems.filter(item => item.enabled === true);
return activeListItems;
});
to:
const sideBarData = useSelector(data => {
const listItems = data.Categories.CategoriesItems;
const activeListItems = listItems.filter(item => item.enabled === true);
return activeListItems;
}, shallowEqual);
useSelector will force a component to re-render when the selector returns a new reference that is different than the previous reference (it uses the === operator). Ref: https://react-redux.js.org/api/hooks#equality-comparisons-and-updates. As you are filtering the array returned from the store, it will always be a different object reference to the one in the store.
The use of shallowEqual as the equalityFn to useSelector() can be used to change the comparison and prevent an unnecessary re-render of the <ProductsPage> component.
did you try using e.preventDefault() otherwise the answer above might work

Forms open for every item in a list when I want to only open a form for that specific item in a list

What the code looks like rendering the button to show the form
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { deleteSong, getSongs, updateSong } from '../../store/song';
import ReactAudioPlayer from 'react-audio-player';
import { useHistory } from 'react-router';
import SongForm from '../AddSongForm';
import EditSongForm from '../EditSongForm';
const SongList = () => {
const [addShowForm, setAddShowForm] = useState(false);
const [editShowForm, setEditShowForm] = useState(false);
const history = useHistory()
const dispatch = useDispatch();
const songsObj = useSelector((state) => state.songState.entries);
const songs = Object.values(songsObj)
const user = useSelector((state) => state.session.user);
const CurrentUserId = user?.id
const remove = (e) => {
dispatch(deleteSong(e.target.id));
}
const addFormCheck = (e) => {
if (addShowForm) setAddShowForm(false)
if (!addShowForm) setAddShowForm(true)
}
const editFormCheck = (e) => {
if (editShowForm) setEditShowForm(false)
if (!editShowForm) setEditShowForm(true)
}
useEffect(() => {
dispatch(getSongs());
}, [dispatch]);
return (
<div>
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm ?
<SongForm />
: null}
</div>
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div className='songdetails' key={id}>
<p key={id}>songName={songName}</p>
<ReactAudioPlayer
src={songLink}
autoPlay
controls
key={songLink}
/>
{userId === CurrentUserId ?
<>
<div>
<button id={id} onClick={remove}>remove</button>
</div>
<div>
<button id={id} onClick={editFormCheck}>edit</button>
{editShowForm ?
<EditSongForm props={id} />
: null}
</div>
</>
: null}
</div>
))}
</ol>
</div>
);
};
export default SongList;
The actual form
import { useState } from "react";
import { useDispatch } from "react-redux";
import { updateSong } from "../../store/song";
import { useSelector } from "react-redux";
const EditSongForm = ({ props }) => {
console.log(props)
const dispatch = useDispatch();
const [songName, setSongName] = useState("");
const [songLink, setSongLink] = useState("");
const [errors, setErrors] = useState([]);
const reset = () => {
setSongName("");
setSongLink("");
// setAlbumName('');
// setArtistName('')
};
const user = useSelector((state) => state.session.user);
const userId = user?.id
const handleSubmit = async (e) => {
e.preventDefault();
const updatedSongDetails = {
id: props,
songName,
songLink,
userId
};
let updatedSong = await dispatch(updateSong(updatedSongDetails))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
reset();
};
return (
<div className="inputBox">
<h1>Update A Song</h1>
<ul>
{errors.map((error, idx) => <li className='errors' key={idx}>{error}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setSongName(e.target.value)}
value={songName}
placeholder="Song Name"
name="Song Name"
/>
<input
type="text"
onChange={(e) => setSongLink(e.target.value)}
value={songLink}
placeholder="Song Link"
name="Audio File"
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default EditSongForm;
Right now when I have a list of songs and click the button for the edit form to appear it applies to the entire list if I have more than one song uploaded. I'm not sure how to make it specific enough so only one form opens at a time.
The solution was to create a component for specific song details and then render that in the .map.
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div>
<SpecificSong id={id} songName={songName} songLink={songLink} userId={userId} />
</div>
))}
</ol>

How to fix a this react filter?

I have created a database in firebase and I have fetch it in react, that is working correctly, but I want to include a search bar to filter the elements, my problem is that when I search for an element everything works but when I delete the text from the search input the elements do not appear again.
import { db } from "../firebase";
import { Link } from "react-router-dom";
import "../App.css";
const Productos = () => {
const [productos, setProductos] = useState([]);
const getLinks = async () => {
db.collection("links").onSnapshot((querySnapshot) => {
const docs = [];
querySnapshot.forEach((doc) => {
docs.push({ ...doc.data(), id: doc.id });
});
setProductos(docs);
});
};
const handelSearch = (e) => {
const cadena = e.target.value.toLowerCase();
const limite = Productos.length;
//console.log(cadena);
let tempArray = [];
for (let i = 0; i < limite; i++) {
const etiquetas = productos[i].description.toLowerCase();
const patron = new RegExp(cadena);
const res = patron.test(etiquetas);
if (res) {
tempArray.push(productos[i]);
}
}
setProductos(tempArray);
};
useEffect(() => {
getLinks();
}, []);
return (
<>
<input
type="text"
placeholder="Buscar"
className="search"
onChange={handelSearch}
name="busqueda"
/>
<div className="productos" name="c" id="c">
<div className="grid-prod">
{productos &&
productos.map((link) => (
<div itemID={link} className="card mb-1" key={link.id}>
<div className="card-body">
<div className="d-flex justify-content-between">
<div className="contenedor-img">
<img
className="img-producto"
alt="producto"
src={link.img}
></img>
</div>
</div>
<h4 className="text-secondary titulo">{link.titulo}</h4>
<h1 className="text-secondary titulo">{link.categoria}</h1>
<Link to={"/" + link.url} rel="noopener noreferrer">
<button className="btn-prod">Ver producto</button>
</Link>
</div>
</div>
))}
</div>
</div>
</>
);
};
export default Productos;```
You can set another state array that keeps track of all the products.
ex:
const [productos, setProductos] = useState([]);
const [allProductos, setAllProductos] = useState([]);
const getLinks = async () => {
db.collection("links").onSnapshot((querySnapshot) => {
const docs = [];
querySnapshot.forEach((doc) => {
docs.push({ ...doc.data(), id: doc.id });
});
setProductos(docs);
setAllProductos(docs);
});
};
function handleSearchChange(event) {
setSearchValue(event.target.value);
search(event.target.value);
}
function search(searchValue) {
setProductos(allProductos);
if(searchValue != ""){
/* perform search logic here */
setProductos(tempArray);
}
}
Try like below, I have used test data from the JSON placeholder
And my App.js file
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Search from "./components/Search"
function App() {
return (
<div className="App">
<Search />
</div>
);
}
export default App;
Search component JS file:
import React from 'react';
class Search extends React.Component{
constructor(){
super();
this.state = {
data:'',
items: []
}
this.filterList = this.filterList.bind(this);
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(
(result) => {
this.setState({data: result});
}
)
}
filterList(event){
var updatedList = this.state.data;
console.log('-----------------',updatedList)
updatedList = updatedList.filter(function(item){
return item.name.toLowerCase().search(
event.target.value.toLowerCase()) !== -1;
});
this.setState({items: updatedList});
}
componentWillMount(){
this.setState({items: this.state.data})
}
render(){
return (
<div>
<form>
<fieldset>
<input type="text" placeholder="Search" onChange={this.filterList}/>
</fieldset>
</form>
<List items={this.state.items}/>
</div>
);
}
}
class List extends React.Component{
render(){
return (
<ul>
{this.props.items &&
this.props.items.map(function(item) {
return <li key={item.name}>{item.name}</li>
})
}
</ul>
)
}
};
export default Search;

Button handler to show preview

Iam doing one of the react assignment and I am stuck on the last part of this assignment. The question is like this: Improve on the application in the previous exercise, such that when the names of multiple countries are shown on the page there is a button next to the name of the country, which when pressed shows the view for that country. Here is my code. I tried some functions but couldnot get it so I wonder if someone can help me to cope with this last part..Thank you
import React, { useState, useEffect } from "react";
import axios from "axios";
import ReactDOM from "react-dom";
const App = () => {
const [countries, setCountries] = useState([]);
const [filter, setFilter] = useState("");
const [select, setSelected] = useState([]);
//console.log(countries);
useEffect(() => {
axios.get("https://restcountries.eu/rest/v2/all").then((response) => {
setCountries(response.data);
});
}, []);
const searchHandler = (e) => {
setFilter(e.target.value);
//console.log(filter);
const selected_countries = countries.filter((item) => {
const letter_case=item.name.toLowerCase().includes(filter.toLowerCase())
return letter_case
});
setSelected(selected_countries);
};
const countryLanguages = (languages)=>
languages.map(language => <li key={language.name}>{language.name}</li>)
const showCountries = () => {
if (select.length === 0) {
return <div></div>
} else if (select.length > 10) {
return "Find the specific filter";
}
else if(select.length>1 && select.length<10){
return (select.map(country=>
<div key={country.alpha3code}>{country.name}
<button>Show</button>//this part
</div>)
)
}
else if(select.length===1){
return(
<div>
<h1>{select[0].name}</h1>
<div>capital {select[0].capital}</div>
<div>population {select[0].population}</div>
<h2>languages</h2>
<ul>{countryLanguages(select[0].languages)}</ul>
<img src={select[0].flag} width="100px"/>
<h2>Weather in {select[0].capital}</h2>
</div>
)
}
};
return (
<div>
<h1>Countries</h1>
find countries: <input value={filter} onChange={searchHandler} />
{showCountries()}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Create a separate component.
const SingleCountry = ({name}) => {
const [ showDetails, setShowDetails ] = useState(false);
const toggleDetails = () => setShowDetails(!showDetails); //toggles the variable true/false
return ( <div>
<button onClick={toggleDetails}>Show Details</button>
{ /* renders the <div> only if showDetails is true */ }
{ showDetails && <div>These are the details of the country {name}</div> }
</div>)
}
Edit your showCountries component to use the new component.
const showCountries = () => {
if (select.length === 0) {
return <div></div>
} else if (select.length > 10) {
return "Find the specific filter";
}
else if(select.length>1 && select.length<10){
return (select.map(country=> <SingleCountry key={country.alpha3code} name={country.name} />
)
}

Categories