How to Render user (multiple) inputs data to table in React. For single table, its easy to use map function and pass it as prop. But, how to approach for multiple inputs?
More specifically, the code below is working for single user input (here, payerName), i need to render all three user inputs to table! (here, inputs namely: PayerName, itemName, AmountSpent)
Thanks!
Code Reference:
File Name: App.js
import { useState } from "react";
import "./styles.css";
import Row from "./Row";
export default function App() {
const [payerName, setPayerName] = useState("");
const [payerNameArray, setPayerNameArray] = useState([]);
const [itemName, setItemName] = useState("");
const [itemNameArray, setItemNameArray] = useState([]);
const [amountSpent, setAmountSpent] = useState("");
const [amountSpentArray, setAmountSpentArray] = useState([]);
const addRows = (e) => {
e.preventDefault();
setPayerNameArray([...payerNameArray, payerName]);
setItemNameArray([...itemNameArray, itemName]);
setAmountSpentArray([...amountSpentArray, amountSpent]);
};
return (
<div className="App">
<input value={payerName} onChange={(e) => setPayerName(e.target.value)} />
<input value={itemName} onChange={(e) => setItemName(e.target.value)} />
<input value={amountSpent} onChange={(e) => setAmountSpent(e.target.value)} />
<button onClick={addRows}>Submit</button>
{payerNameArray.map((payee, index) => (
<Row payer_name={payee} ukey={index} />
))}
</div>
);
}
File Name: Row.js
const Row = (props) => {
return (
<div key={props.ukey}>
<tr>
<td>{props.payer_name}</td>
</tr>
</div>
);
};
export default Row;
Thanks!
Related
I have a form in a page, when the user inputs the name of a new student and clicks submit, I want the content of that component (the form) to be completely replaced by the submitted name. How can I achieve this (Replace the form with the list onsubmit)?
I have read that I can use conditional rendering to toggle components, but it's not really clear to me how i can apply it here.
StudentListResult.Jsx
import React, { useState } from "react";
import StudentForm from "./StudentForm";
import StudentList from "./StudentList";
const StudentListResult = () => {
const [newStudent, setNewStudent] = useState("");
const [students, setStudentsList] = useState([]);
return (
<div>
<div>
<StudentForm
newStudent={newStudent}
setNewStudent={setNewStudent}
students={students}
setStudentsList={setStudentsList}
/>
</div>
<div>
<StudentList students={students} setStudentsList={setStudentsList} />
</div>
</div>
);
};
export default StudentListResult;
StudentListForm
import React from "react";
import { v4 as uuidv4 } from "uuid";
const StudentListForm = ({
newStudent,
setNewStudent,
students,
setStudentsList,
}) => {
const addStudent = (event) => {
event.preventDefault();
setStudentsList([...students, { id: uuidv4(), name: newStudent }]);
setNewStudent("");
};
return (
<form onSubmit={addStudent}>
<div>
<input
value={newStudent}
type="text"
placeholder="Student Name"
onChange={(e) => setNewStudent(e.target.value)}
/>
</div>
<div>
<button>Submit</button>
</div>
</form>
);
};
export default StudentListForm;
StudentList.jsx
import React from "react";
const StudentList = ({ students = [], setStudentsList }) => {
return (
<div>
{students.map((student) => (
<ul key={student.id}>
<li>
<p>{student.name}</p>
</li>
</ul>
))}
</div>
);
};
export default StudentList;
So you want to show the form if not submitted and show the list if submitted? You can add a piece of state called submitted and do simple conditional rendering.
const StudentListResult = () => {
const [submitted, setSubmitted] = useState(false)
return (
{submitted ? <StudentList /> : <StudentListForm />}
);
};
And then in your addStudent function, set submitted.
const addStudent = (event) => {
// ...
setSubmitted(true)
}
If you want change form and list visibility state, you need pass custom function to form component:
StudentListResult.jsx:
const StudentListResult = () => {
const [newStudent, setNewStudent] = useState("");
const [students, setStudentsList] = useState([]);
const [getFormSubmitted, setFormSubmitted] = useState(false);
const setCompletedForm = () => {
setFormSubmitted(!getFormSubmitted);
};
return (
<div>
{getFormSubmitted ? (
<div>
<StudentList students={students} setStudentsList={setStudentsList} />
</div>
) : (
<div>
<StudentForm
newStudent={newStudent}
setNewStudent={setNewStudent}
students={students}
setStudentsList={setStudentsList}
onComplete={setCompletedForm}
/>
</div>
)}
</div>
);
};
Then call this function if form is submitted and all conditions is true
StudentListForm.tsx:
const StudentListForm = ({
newStudent,
setNewStudent,
students,
setStudentsList,
onComplete
}) => {
const addStudent = (event) => {
event.preventDefault();
setStudentsList([...students, { id: uuidv4(), name: newStudent }]);
setNewStudent("");
onComplete();
};
I don't understand why my page can't recognize other pages when I click (for example on page 2, the same page appears again and again)
This is in MealNew.js component:
import React, {useEffect, useState } from "react";
import './MealNew.css';
import Card from "../UI/Card";
import AppPagination from "./AppPagination";
const MealNew = () => {
const [data, setData] = useState([]);
const [showData, setShowData] = useState(false);
const [query,setQuery] = useState('');
const[page,setPage] = useState(9);
const[numberOfPages,setNumberOfPages]= useState(10);
const handleClick = () => {
setShowData(true);
const link = `https://api.spoonacular.com/recipes/complexSearch?query=${query}&apiKey=991fbfc719c743a5896bebbd98dfe996&page=${page}`;
fetch (link)
.then ((response)=> response.json())
.then ((data) => {
setData(data.results)
setNumberOfPages(data.total_pages)
const elementFood = data?.map((meal,key) => {
return (<div key={key}>
<h1>{meal.title}</h1>
<img src={meal.image}
alt='e-meal'/>
</div> )
})
const handleSubmit = (e) => {
e.preventDefault();
handleClick();
}
useEffect(()=> {
handleClick();
},[page])
return (
<Card className="meal">
<form onSubmit={handleSubmit}>
<input
className="search"
placeholder="Search..."
value={query}
onChange={(e)=>setQuery(e.target.value)}/>
<input type='submit' value='Search'/>
</form>
<li className="meal">
<div className = 'meal-text'>
<h5>{showData && elementFood}</h5>
<AppPagination
setPage={setPage}
pageNumber={numberOfPages}
/>
</div>
</li>
</Card>
) }
export default MealNew;
This is in AppPagination.js component:
import React from "react";
import { Pagination } from "#mui/material";
const AppPagination = ({setPage,pageNumber}) => {
const handleChange = (page)=> {
setPage(page)
window.scroll(0,0)
console.log (page)
}
return (
<div >
<div >
<Pagination
onChange={(e)=>handleChange(e.target.textContent)}
variant="outlined"
count={pageNumber}/>
</div>
</div>
)
}
export default AppPagination;
Thanks in advance, I would appreciate it a lot
The only error I am getting in Console is this:
Line 64:3: React Hook useEffect has a missing dependency: 'handleClick'. Either include it or remove the dependency array react-hooks/exhaustive-deps
You are not following the spoonacular api.
Your link looks like this:
https://api.spoonacular.com/recipes/complexSearch?query=${query}&apiKey=<API_KEY>&page=${page}
I checked the spoonacular Search Recipes Api and there's no page parameter you can pass. You have to used number instead of page.
When you receive response from the api, it returns the following keys: offset, number, results and totalResults.
You are storing totalResults as totalNumberOfPages in state which is wrong. MUI Pagination count takes total number of pages not the total number of records. You can calculate the total number of pages by:
Math.ceil(totalRecords / recordsPerPage). Let say you want to display 10 records per page and you have total 105 records.
Total No. of Pages = Math.ceil(105/10)= 11
Also i pass page as prop to AppPagination component to make it as controlled component.
Follow the documentation:
Search Recipes
Pagination API
Complete Code
import { useEffect, useState } from "react";
import { Card, Pagination } from "#mui/material";
const RECORDS_PER_PAGE = 10;
const MealNew = () => {
const [data, setData] = useState([]);
const [showData, setShowData] = useState(false);
const [query, setQuery] = useState("");
const [page, setPage] = useState(1);
const [numberOfPages, setNumberOfPages] = useState();
const handleClick = () => {
setShowData(true);
const link = `https://api.spoonacular.com/recipes/complexSearch?query=${query}&apiKey=<API_KEY>&number=${page}`;
fetch(link)
.then((response) => response.json())
.then((data) => {
setData(data.results);
const totalPages = Math.ceil(data.totalResults / RECORDS_PER_PAGE);
setNumberOfPages(totalPages);
});
};
const elementFood = data?.map((meal, key) => {
return (
<div key={key}>
<h1>{meal.title}</h1>
<img src={meal.image} alt='e-meal' />
</div>
);
});
const handleSubmit = (e) => {
e.preventDefault();
handleClick();
};
useEffect(() => {
handleClick();
console.log("first");
}, [page]);
return (
<Card className='meal'>
<form onSubmit={handleSubmit}>
<input className='search' placeholder='Search...' value={query} onChange={(e) => setQuery(e.target.value)} />
<input type='submit' value='Search' />
</form>
<li className='meal'>
<div className='meal-text'>
<h5>{showData && elementFood}</h5>
<AppPagination setPage={setPage} pageNumber={numberOfPages} page={page} />
</div>
</li>
</Card>
);
};
const AppPagination = ({ setPage, pageNumber, page }) => {
const handleChange = (page) => {
setPage(page);
window.scroll(0, 0);
console.log(page);
};
console.log("numberOfPages", pageNumber);
return (
<div>
<div>
<Pagination
page={page}
onChange={(e) => handleChange(e.target.textContent)}
variant='outlined'
count={pageNumber}
/>
</div>
</div>
);
};
export default MealNew;
I have custom component which I am importing in my another Component as a Element tag. My custom Component consist of dropdown values. I want to read value the value of in my element tag when I submit my form
custom component :
import React, { useState, useMemo } from 'react'
import Select from 'react-select'
import countryList from 'react-select-country-list'
function CountrySelector() {
const [value, setValue] = useState('')
const options = useMemo(() => countryList().getData(), [])
const changeHandler = value => {
setValue(value)
}
return <Select options={options} value={value} onChange={changeHandler} />
}
export default CountrySelector
i want to use that custom component country selector values on my submit button:
main component:
import react from 'react';
import CountrySelector from '../../helpers/CountrySelector';
import IdType from '../../helpers/IdType';
import ProofOfAddress from '../../helpers/ProofOfAddress';
const submitForm=(e)=>{
//debugger;
e.preventDefault();
console.warn(e.target)
};
const IdentityVerification = (props) => {
const saveUser=(e)=>{
console.warn({e});
}
return (
<form onSubmit={submitForm} >
<div className='app'>
<label >Choose Issuing Country/region</label>
<CountrySelector/>
<label >Select ID Type</label>
<IdType/>
<label >Proof of Address</label>
<ProofOfAddress/>
</div>
<div className="form-actions">
<button >Submit</button>
</div>
</form>
);
};
export default IdentityVerification;
how can i read values?
The normal way to handle this would be to move the state and your changeHandler function into the parent component and pass the handler down to the child as a prop.
const IdentityVerification = (props) => {
const [value, setValue] = useState('')
const changeHandler = value => {
setValue(value)
}
return (
// ...
<CountrySelector onChange={changeHandler}/>
// ...
);
and in your child:
function CountrySelector({changeHandler}) {
// ....
return <Select options={options} value={value} onChange={changeHandler} />
}
What i want to do :
When i click my button i.e Search in Navbar.js i want to assign the search text in the variable urlQuery so i can pass it as props in Episodes.js component
End goal is to pass the urlQuery from Navbar.js somehow to Episodes.js component so i can query the REST api
How do i achieve the desired behaviour pls help
App.js
import React, { useState } from 'react';
import './App.css'
import Episodes from './components/Episodes/Episodes'
import CustomNavbar from './components/Navbar/Navbar'
import Pagination from './components/Pagination/Pagination'
function App() {
const [postsPerPage] = useState(20);
const [currentPage, setCurrentPage] = useState(1);
const url=`https://rickandmortyapi.com/api/episode?page=${currentPage}`
let urlQuery = `https://rickandmortyapi.com/api/episode?name=${SEARCH TEXT HERE}`
const paginate = pageNumber => setCurrentPage(pageNumber);
return (
<div>
<CustomNavbar />
<Episodes
urlQuery={urlQuery}
url={url}
/>
<Pagination
postsPerPage={postsPerPage}
totalPosts={36}
paginate={paginate}
/>
</div>
);
}
export default App;
Navbar.js
import React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import FormControl from 'react-bootstrap/FormControl';
const customNavbar = () => {
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">Rick And Morty</Navbar.Brand>
<Form inline>
<FormControl type="text" placeholder="Search" />
<Button>Search</Button>
</Form>
</Navbar>
);
}
export default customNavbar
Edit
On Zohaib's suggestion this error is thrown
Failed to compile.
./src/components/Navbar/Navbar.js
Line 14:48: Unexpected use of 'event' no-restricted-globals
Search for the keywords to learn more about each error.
App.js
import React, { useState, useEffect } from 'react';
import './App.css'
import Episodes from './components/Episodes/Episodes'
import CustomNavbar from './components/Navbar/Navbar'
import Pagination from './components/Pagination/Pagination'
function App() {
const [postsPerPage] = useState(20);
const [currentPage, setCurrentPage] = useState(1);
const [userSearchValue, setUserSearchValue] = useState('');
const [url, setUrl] = useState(``);
const [urlQuery, setUrlQuery] = useState(``)
useEffect(() => {
setUrl(`https://rickandmortyapi.com/api/episode?page=${currentPage}`)
}, [currentPage]);
useEffect(() => {
setUrlQuery(`https://rickandmortyapi.com/api/episode?name=${userSearchValue}`)
}, [userSearchValue])
const paginate = pageNumber => setCurrentPage(pageNumber);
const handleButtonClick = (searchValue) => {
setUserSearchValue(searchValue);
}
return (
<div>
<CustomNavbar
onButtonClick={handleButtonClick}
/>
<Episodes
urlQuery={urlQuery}
url={url}
/>
<Pagination
postsPerPage={postsPerPage}
totalPosts={36}
paginate={paginate}
/>
</div>
);
}
export default App;
Navbar.js
import React, { useState } from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import FormControl from 'react-bootstrap/FormControl';
const customNavbar = ({ onButtonClick }) => {
const [searchValue, setSearchValue] = useState('');
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">Rick And Morty</Navbar.Brand>
<Form inline>
<FormControl type="text" placeholder="Search" value={searchValue} onChange={(e) => setSearchValue(e.target.value)} />
<Button onClick={() => onButtonClick(searchValue)}>Search</Button>
</Form>
</Navbar>
);
}
export default customNavbar
The important part here is you're passing down the handleButtonClick function to the child component (Navbar). This way you can call that parent function in the child component whenever you want (ie. when the user clicks the submit button).
Do you mean something like this?
There is a React guide about this specific problem: Lifting State Up.
Normally what you do is you manage the state in the parent. In this case App where you manage the search text state. You pass down a function to components to change this state. The components that depend upon this state are passed the value through the properties.
Here is an example:
const {useEffect, useState} = React;
function App() {
const episodesURL = "https://rickandmortyapi.com/api/episode";
const [page, setPage] = useState(1);
const [pageInfo, setPageInfo] = useState({});
const [searchText, setSearchText] = useState("");
const [episodes, setEpisodes] = useState([]);
useEffect(() => {
const url = new URL(episodesURL);
url.searchParams.set("page", page);
if (searchText) url.searchParams.set("name", searchText);
fetch(url)
.then(response => response.json())
.then(response => {
if (response.error) {
setPageInfo({});
setEpisodes([]);
} else {
setPageInfo(response.info);
setEpisodes(response.results);
}
});
}, [page, searchText]);
const search = searchText => {
setSearchText(searchText);
setPage(1);
};
return (
<div>
<CustomNavbar search={search} />
<Episodes episodes={episodes} />
<Pagination setPage={setPage} info={pageInfo} />
</div>
);
}
function CustomNavbar({search}) {
const [searchText, setSearchText] = useState("");
const handleFormSubmit = event => {
event.preventDefault();
search(searchText);
};
return (
<form onSubmit={handleFormSubmit}>
<input
type="text"
placeholder="search"
value={searchText}
onChange={event => setSearchText(event.target.value)}
/>
<button type="submit">Search</button>
</form>
);
}
function Episodes({episodes}) {
return (
<table>
<thead>
<tr>
<th>episode</th>
<th>name</th>
<th>air date</th>
</tr>
</thead>
<tbody>
{episodes.map(episode => (
<tr key={episode.id}>
<td>{episode.episode}</td>
<td>{episode.name}</td>
<td>{episode.air_date}</td>
</tr>
))}
</tbody>
</table>
);
}
function Pagination({setPage, info}) {
return (
<div>
{info.prev && <a onClick={() => setPage(page => page - 1)}>previous</a>}
{info.next && <a onClick={() => setPage(page => page + 1)}>next</a>}
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
th { text-align: left; }
a { cursor: pointer; }
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Change urlQuery to state variable. Then, pass setUrlQuery to NavBar as a prop and on search button clickEvent call setUrlQuery function.
I have a form on 'products/add' and I add products to the database, after I submit the request, I redirect to the page where all the products are displayed. However, this page does not display information about the last item I added. How to fix it? How do I render pages after redirects to display the most current data?
'localhost:3333/products/add'
import React, {useState} from 'react';
import api from './api';
import { Redirect } from 'react-router'
const HandleProduct = () => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [redirect, setRedirect] = useState(false);
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
const addProduct = (e) =>{
e.preventDefault();
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
console.log(res);
setRedirect(true);
})
}
if(redirect) {
return <Redirect to={'/products'} />
}
return (
<div>
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
export default HandleProduct;
List of products(localhost:3333/products):
import React, {useContext} from 'react';
import {ProductsContext} from './ProductsContext';
const ProductsList = () => {
const [data] = useContext(ProductsContext);
return (
<div>
{console.log(data)}
{data.products.map((product, index)=>(
<div key={index}>
<p>{product.name}</p>
<p><i>{product.description}</i></p>
</div>
))}
</div>
);
}
export default ProductsList;