How to add data into an array using inputs in reactjs? - reactjs

I am new to react and I am trying to build a todo app which basically can execute the CRUD operation. I am able to update and delete as of now. But not sure how to create an object and save it into the array.
When i click on edit or add task button a modal pop up and i am trying to enter the title and description value there.
This is my Index.js file
import React, {Component} from 'react';
import { Button, Modal } from 'reactstrap';
import Modals from './Modals'
const todoItems = [
{
id: 1,
title: "Go to Market",
description: "Buy ingredients to prepare dinner",
completed: true
},
{
id: 2,
title: "Study",
description: "Read Algebra and History textbook for upcoming test",
completed: false
},
{
id: 3,
title: "Sally's books",
description: "Go to library to rent sally's books",
completed: true
},
{
id: 4,
title: "Article",
description: "Write article on how to use django with react",
completed: false
}
];
class Index extends Component {
state = {
modal: false,
items: todoItems,
selectedItem: null,
selectedIndex: -1,
}
toggle = (item, index) => {
if (item) {
this.setState({ selectedItem: item, selectedIndex: index })
}
this.setState({ modal: !this.state.modal });
};
handleChange = () => {
let oldState = this.state.items;
this.setState({ items: oldState })
}
onDelete = (item) => {
const newData = this.state.items.filter(i => i.title !== item.title)
this.setState({ items: newData })
}
render() {
return (
<>
<h1 className="p-3">TODO APP</h1>
<div style={{ backgroundColor: "white", padding: "50px", color: "black"}} className="container">
<div className="row">
<button className="btn btn-primary" onClick={() => this.toggle()}>Add task</button>
</div>
<div className="row my-5">
<Button color="danger mr-5">Incomplete</Button>
<Button color="success">Complete</Button>
<Modals index={this.state.selectedIndex} onDelete={this.onDelete} item={this.state.selectedItem} handleChange={this.handleChange} modal={this.state.modal} toggle={this.toggle} />
</div>
<hr/>
{this.state.items.map((item, index) => {
return(
<div key={item.id}>
<div className="row">
<p style={{ textAlign: "left" }}>{item.title}</p>
<Button onClick={() => this.toggle(item, index)} className="mr-0 ml-auto" color="info">Edit</Button>
<Button onClick={() => this.onDelete(item)} className="ml-5" color="warning">Delete</Button>
</div>
<hr/>
</div>
)
})}
</div>
</>
);
}
}
export default Index;
This is my modal.js file
import React, { useState, useEffect } from 'react';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
function Modals(props) {
const {
className,
buttonLabel,
handleChange,
item,
index,
toggle,
} = props;
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
useEffect(() => {
if (item && item.title){
setTitle(item.title)
setDescription(item.description)
}
}, [item])
const setValues = () => {
handleChange({ title: title, description: description });
props.toggle();
push({
})
}
return (
<div>
<Modal isOpen={props.modal} className={className} style={{ color: "black"}}>
<ModalHeader >Todo Item</ModalHeader>
<ModalBody>
<div className="container-fluid">
<div className="row mb-3">
<div className="col-12">
<p className="mb-0">Title</p>
</div>
<div className="col-12">
<input onChange={(e) => setTitle(e.target.value)} value={title} className="w-100" type="text" placeholder="Enter title"/>
</div>
</div>
<div className="row mb-3">
<div className="col-12">
<p className="mb-0">Description</p>
</div>
<div className="col-12">
<input type="text" onChange={(e) => setDescription(e.target.value)} value={description} className="w-100" placeholder="Enter Description"/>
</div>
</div>
<div className="row">
<div className="col-12">
<input type="checkbox"/>
<span className="ml-2"> Completed </span>
</div>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button onClick={() => setValues()} color="success">Submit</Button>
<Button onClick={props.toggle} color="secondary">Cancel</Button>
</ModalFooter>
</Modal>
</div>
)
}
export default Modals;
Thanks in advance!!

One way would be to just create a method in index.js
addItem = (item) =>{
this.setState({items: [...this.state.items, item]})
}
and then just pass it as a prop to your Modal and call it in setValues,
const setValues = () => {
handleChange({ title: title, description: description });
props.toggle();
props.addItem({ title: title, description: description, //etc})
}

Related

How can I maintain the order of adding items in react web app?

How can I create an order of adding items. I should be able to add one after the other item during on click. By default it should display the Textarea first and then blockquote ( see below )
a) When a user click on Text area button, it should add one after the blockquote.
b) Then when the user clicks on Code area button, it should add after Textarea. Could someone please advise ?
CSB link: https://codesandbox.io/s/distracted-worker-26jztf?file=/src/App.js
Something similar >> Expected behaviour: https://jsfiddle.net/nve8qdbu/8/
import "./styles.css";
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useForm } from "react-hook-form";
const blogListData = [
{
id: 1,
heading: "React state",
date: "22-May-2022",
tag: "React",
count: "3"
},
{
id: 2,
heading: "Cypress testing detailss",
date: "22-May-2022",
tag: "Cypress",
count: "5"
}
];
const Admin = () => {
const [createImageTag, setImageTag] = useState("");
const [fields, setFields] = useState([{ value: null }]);
const [createCode, setCreateCode] = useState([{ value: null }]);
const [blogList, setBlogList] = useState([]);
const {
register,
handleSubmit,
formState: { errors },
reset
} = useForm();
useEffect(() => {
setBlogList(blogListData);
}, []);
function handleChangeTextArea(i, event) {
const values = [...fields];
values[i].value = event.target.value;
setFields(values);
}
function handleChangeCode(i, event) {
const codeValues = [...createCode];
codeValues[i].value = event.currentTarget.innerText;
setCreateCode(codeValues);
}
function handleTextAreaAdd() {
const values = [...fields];
values.push({ value: null });
setFields(values);
}
function handleCodeAreaAdd() {
const codeValues = [...createCode];
codeValues.push({ value: null });
setCreateCode(codeValues);
}
function handleImageAreaAdd() {
const image = [...createImageTag];
image.push({ value: null });
setCreateCode(image);
}
function handleRemoveText(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function handleRemoveCode(i) {
const codeValues = [...createCode];
codeValues.splice(i, 1);
setCreateCode(codeValues);
}
const handleLogout = () => {
localStorage.removeItem("loginEmail");
};
return (
<div id="App">
<div className="parent">
<div className="adminSection">
<h1>Create a new blog</h1>
<div className="row">
<div className="logout">
<img
src="/images/logout.png"
alt="Logout"
onClick={handleLogout}
></img>
</div>
<div className="createBlogSection">
<div className="row">
<button
onClick={() => handleTextAreaAdd()}
className="textAreaBtn"
>
Text Area
</button>
<button
onClick={() => handleCodeAreaAdd()}
className="codeAreaBtn"
>
Code Area
</button>
<button
onClick={() => handleImageAreaAdd()}
className="imageAreaBtn"
>
Add Image
</button>
</div>{" "}
<br></br>
<div className="row">
{fields.map((field, idx) => {
return (
<div key={`${field}-${idx}`} className="dtextArea">
<button
type="button"
onClick={() => handleRemoveText(idx)}
className="closeElement"
>
X
</button>
<textarea
type="text"
id="blogtext"
placeholder="Enter your text here"
className="defaultTextArea"
{...register("blogtext", {
required: true,
minLength: {
value: 25,
message: "Minimum length of 25 letters"
}
})}
value={field.value || ""}
onChange={(e) => handleChangeTextArea(idx, e)}
/>
<span className="validationmsg">
{errors.blogtext &&
errors.blogtext.type === "required" && (
<span>Blog text is required !</span>
)}
{errors.blogtext && (
<span>{errors.blogtext.message}</span>
)}
</span>
</div>
);
})}
</div>
<div className="row">
{createCode.map((code, idx) => {
return (
<div key={`${code}-${idx}`} className="dCodeArea">
<button
type="button"
onClick={() => handleRemoveCode(idx)}
className="closeElement"
>
X
</button>
<blockquote
type="text"
id="blogCode"
contentEditable="true"
className="codehighlight"
placeholder="Enter your code here"
{...register("blogCode", {
required: true
})}
value={code.value || ""}
onInput={(e) => handleChangeCode(idx, e)}
/>
</div>
);
})}
</div>
<div className="row">
</div>
<div className="row">
<div className="submitSection">
<input type="submit" className="submitBtn" />
</div>
</div>
</div>
</div>
</div>
<div className="blogListSection">
<h1>Edit blogs</h1>
<div className="row">
<div className="editBlogSection">
{blogList.map(({ id, heading, count }) => (
<a
key={id}
href="https://www.testingsite.com/"
className="blogitems"
>
<pre>
<span>{count}</span> {heading}
</pre>
</a>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default Admin;
react is designed for components . each of your list elements should be refactored by a component.then it would be easier. i think a single react component could do the trick

How to validate a table with arrays using SimpleReactValidator

I have a table that can add and delete rows. However, am not able to validate the table. if the table cells are empty or filled am not able to post the values to the backend. The error message still shows.What is the best way to validate the table. I would like to have the error message show if the fields are empty on submit.
payment.js
import React, {Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css"
import axios from "axios"
import SimpleReactValidator from "simple-react-validator"
import Box from '#mui/material/Box';
import Button from "#mui/material/Button";
import BillTable from "./billTable"
import $ from 'jquery'
class Payment extends Component {
constructor(){
super()
this.state = {
Bill:"",
billTable: [{
index: Math.random(),
serialNumber: "",
item:"",
cost:""}],
errorMessage: '',
}
this.validator = new SimpleReactValidator({autoForceUpdate: this,messages: {
default: 'Please enter this field!'
},element: message => <div style={{color: "red"}}>{message}</div>})
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange = (e) => {
if (["serialNumber", "item", "cost"].includes(e.target.name)) {
let billTable = [...this.state.billTable]
billTable[e.target.dataset.id][e.target.name] = e.target.value;
}
else {
this.setState({ [e.target.name]: e.target.value })
}
};
addNewRowPlan = () => {
this.setState((prevState) => ({
billTable: [...prevState.billTable, { index: Math.random(), serialNumber: "", item: "", cost:""}],
}));
}
deleteRow = (index) => {
this.setState({
billTable: this.state.billTable.filter((s, sindex) => index !== sindex),
});
}
clickOnDelete(record) {
this.setState({
billTable: this.state.billTable.filter(r => r !== record),
});
}
handleSubmit(event){
event.preventDefault()
if(this.validator.allValid()){
this.validator.hideMessages()
const billed = {
Bill:this.state.billTable
};
axios.post(`http://localhost:4000/bill`, billed,{
})
.then(response => {
console.log(response.data)
return $('.alert-success').show();
})
this.setState({
Bill:"",
})
}
else{
this.validator.showMessages()
this.forceUpdate()
return $('.alert-danger').show();
}
}
render() {
let {billTable} = this.state
return (
<div>
<div className="container">
<div className="form-div">
<h1>Billing</h1>
<Box component="form" onSubmit={this.handleSubmit} noValidate sx={{ mt: 1}} onChange={this.handleChange}>
<div className="row" style={{ marginTop: 20 }}>
<div className="col-sm-3"></div>
<div className="col-sm-12">
<div className="card">
<div className="card-header text-center">Bill</div>
<div className="card-body">
<div className="row">
<div className="col-sm-4">
</div>
</div>
<table>
<thead>
<tr>
<th>Serial #</th>
<th>Item</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
<BillTable id="bill" add={this.addNewRowPlan.bind(this)} delete={this.clickOnDelete.bind(this)} billTable={billTable} />
{this.validator.message('bill',this.state.Bill,'required')}
</tbody>
</table>
</div>
</div>
</div>
<div className="col-sm-1"></div>
</div>
<Button
type="submit"
fullWidth
sx={{ mt: 3, mb: 2}}
>
<span>Submit</span>
</Button>
</Box>
</div>
</div>
</div>
);
}
}
export default Payment;
billTable.js
import React from "react"
const billTable = (props) => {
return (props.billTable.map((val, idx) => {
let serialNumber = `serialNumber-$${idx}`, item = `item-$${idx}`, cost = `cost-$${idx}`
return (
<tr key={val.index}>
<td>
<input type="text" name="serialNumber" data-id={idx} id={serialNumber} className="form-control" />
</td>
<td>
<input type="text" name="item" data-id={idx} id={item} className="form-control " />
</td>
<td>
<input type="text" name="cost" data-id={idx} id={cost} className="form-control"/>
</td>
<td>
{
idx===0?<button onClick={()=>props.add(idx)} type="button" className="btn btn-primary">Add Row</button>
: <button className="btn btn-danger" onClick={(() => props.delete(val))} >Delete Row</button>
}
</td>
</tr>
)
})
)
}
export default billTable
`````````````````````
How can i correct this code. Thank you in advance

Why is my onSubmit not working in PrimeReact Dialog?

My code is no longer submitting data when onSubmit is pushed even though it was when I was using a and tag:
<Modal><Form onSubmit={ saveProject }></Form></Modal>
The only thing different now is I substituted Modal for as I am using PrimeReact and deleted the tag. Now it's no longer submitting data.
Can anyone tell me why it is no longer submitting the data to the backend?
/Projects.js
import React, { useState, useEffect } from "react";
import { Column } from "primereact/column";
import { DataTable } from "primereact/datatable";
import { Button } from "primereact/button";
// import { Modal, ModalFooter, ModalHeader } from "react-bootstrap";
import { InputText } from "primereact/inputtext";
import { InputTextarea } from "primereact/inputtextarea";
// import { InputNumber } from "primereact/inputnumber";
import { Dropdown } from "primereact/dropdown";
import { Dialog } from "primereact/dialog";
import axios from "axios";
const Projectz = () => {
const [ticket_title, setTicketTitle] = useState("");
const [ticket_description, setTicketDescription] = useState("");
// const [time_takes, setTimeTakes] = useState("");
const [type_menu, setTypeMenu] = useState("");
const [priority_menu, setPriorityMenu] = useState("");
const [status_menu, setStatusMenu] = useState("");
const [projects, setProjects] = useState([]);
//part of modal
const [displayResponsive, setDisplayResponsive] = useState(false);
// const [position, setPosition] = useState("center");
useEffect(() => {
getProjects();
}, []);
const getProjects = async () => {
const response = await axios.get("http://localhost:5002/ticket_table");
setProjects(response.data);
};
const saveProject = async (e) => {
e.preventDefault();
await axios.post("http://localhost:5002/ticket_table", {
ticket_title: ticket_title,
ticket_description: ticket_description,
// time_takes: time_takes,
type_menu: type_menu,
priority_menu: priority_menu,
status_menu: status_menu,
});
};
const dropdownValues1 = [{ value: "Issue" }, { value: "Bug" }, { value: "Error" }, { value: "Other" }];
const dropdownValues2 = [{ value: "Low" }, { value: "Medium" }, { value: "High" }, { value: "Immediate" }];
const dropdownValues3 = [{ value: "New" }, { value: "Open" }, { value: "In Progress" }, { value: "Resolved" }, { value: "Additional Info Required" }];
const dialogFuncMap = {
displayResponsive: setDisplayResponsive,
};
const onClick = (name, position) => {
dialogFuncMap[`${name}`](true);
};
const onHide = (name) => {
dialogFuncMap[`${name}`](false);
};
const renderFooter = (name) => {
return (
<div>
<Button label="Submit" className="p-button-rounded p-button-success mr-2 mb-2 success" />
</div>
);
};
// const [show, setShow] = useState(false);
// const handleClose = () => setShow(false);
// const handleShow = () => setShow(true);
return (
<>
<div className="grid table-demo">
<div className="col-12">
<div className="card">
<h5>Tickets</h5>
<div>
{/* <Button label="New Ticket" className="p-button-rounded mr-2 mb-2 npbutton" onClick={handleShow} /> */}
<Button className="p-button-rounded mr-2 mb-2 npbutton" label="New Ticket" onClick={() => onClick("displayResponsive")} />
</div>
<Dialog className="dialogModal" header="Create Ticket" visible={displayResponsive} onHide={() => onHide("displayResponsive")} breakpoints={{ "960px": "75vw" }} style={{ width: "30vw" }} footer={renderFooter("displayResponsive")}>
<form onSubmit={saveProject}>
<h5>Ticket Name</h5>
<InputText value={ticket_title} onChange={(e) => setTicketTitle(e.target.value)} type="text" placeholder="Enter ticket name"></InputText>
<h5>Ticket Description</h5>
<InputTextarea value={ticket_description} onChange={(e) => setTicketDescription(e.target.value)} type="text" placeholder="Enter ticket description" autoResize rows="3" cols="30" />
{/* <h5>Time Estimate (Hours)</h5> */}
{/* <InputNumber value={time_takes} onValueChange={(e) => setTimeTakes(e.value)} showButtons mode="decimal"></InputNumber> */}
<h5>Type</h5>
<Dropdown value={type_menu} onChange={(e) => setTypeMenu(e.value)} options={dropdownValues1} optionLabel="value" placeholder="Select" />
<h5>Priority</h5>
<Dropdown value={priority_menu} onChange={(e) => setPriorityMenu(e.value)} options={dropdownValues2} optionLabel="value" placeholder="Select" />
<h5>Status</h5>
<Dropdown value={status_menu} onChange={(e) => setStatusMenu(e.value)} options={dropdownValues3} optionLabel="value" placeholder="Select" />
</form>
</Dialog>
<div>
<DataTable
// sortMode="single" sortField="representative.name"
sortOrder={1}
scrollable
scrollHeight="400px"
responsiveLayout="scroll"
>
<Column field="ticket_title" header="Ticket Title" style={{ minWidth: "200px" }}></Column>
<Column field="description" header="Description" style={{ minWidth: "350px" }}></Column>
<Column field="status" header="Status" style={{ minWidth: "200" }}></Column>
<Column field="createdAt" header="Date" style={{ minWidth: "200px" }}></Column>
</DataTable>
</div>
</div>
</div>
</div>
<div className="grid table-demo">
<div className="col-12">
<div className="card">
<h5>Ticket Info</h5>
<div>
<DataTable
// value={projects}
// sortMode="single" sortField="representative.name"
// sortOrder={1}
// scrollable
// scrollHeight="400px"
// responsiveLayout="scroll"
>
{projects.map((project, index) => (
<tr key={project.id}>
<td>{index + 1}</td>
<td>{project.ticket_title}</td>
<td>{project.ticket_description}</td>
{/* <td>{ticket.time_takes}</td> */}
<td>{project.type_menu}</td>
<td>{project.priority_menu}</td>
<td>{project.status_menu}</td>
</tr>
))}
</DataTable>
</div>
</div>
</div>
</div>
</>
);
};
export default React.memo(Projectz);
This is because the Dialog renders renderFooter outside of your <form>. I ran into the same issue.
You can fix it instead of using the footer just rendering your button to look like its in the footer.
<Dialog className="dialogModal" header="Create Ticket" visible={displayResponsive} onHide={() => onHide("displayResponsive")} breakpoints={{ "960px": "75vw" }} style={{ width: "30vw" }}>
<form onSubmit={saveProject}>
....
<div className="p-dialog-footer pb-0">
<Button label="Submit" type="submit" className="p-button-rounded p-button-success mr-2 mb-2" />
</div>
</form>
</Dialog>

I want to use react hook 'useState' to save the information fetched from API and show on the scrren. But, I can't use in class so what can I do?

So, for some context I'm fetching some search results on click on search button. The result shows in console.log() after clicking search button perfectly. the SearchComponent.js is below.
import React, { Component, useState } from 'react'
import { API_KEY, API_URL } from '../config/keys';
import { Link } from 'react-router-dom';
class SearchBox extends Component {
constructor(props) {
super(props);
this.state = {
searchQuery: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.onChangeValue = this.onChangeValue.bind(this);
}
handleChange = (e) => {
e.preventDefault();
const { name, value } = e.target;
this.setState({ [name]: value });
}
handleSubmit = (e) => {
e.preventDefault();
var data = this.state.searchQuery;
const url = `${API_URL}search/${this.state.selectedOption}?api_key=${API_KEY}&language=en-US&query=${encodeURI(data)}&page=1&include_adult=false`;
fetch(url)
.then(response => response.json())
.then(response => {
console.log(response);
const result = response.results;
})
}
onChangeValue(event) {
this.setState({
selectedOption: event.target.value
});
}
render() {
return (
<>
<div className="mt-3">
{/* Breadcrumb */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<nav aria-label="breadcrumb">
<ol className="breadcrumb">
<li className="breadcrumb-item"><Link to='/'>Home</Link></li>
<li className="breadcrumb-item active" aria-current="page">Search</li>
</ol>
</nav>
</div>
<div className="row">
<div className="col-6 offset-3">
<form className="form-group">
<div className="input-group">
<input
className="form-control"
type="text"
placeholder= 'Search...'
onChange={this.handleChange}
name= 'searchQuery'
value={this.state.searchQuery} />
<button onClick={this.handleSubmit} type="submit" className="btn btn-primary input-group-addon" ><span className="fa fa-search fa-lg"></span></button>
</div>
{/* radio buttons */}
<div className="mt-2">
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="movie"
value="movie"
checked={this.state.selectedOption === "movie"}
onChange={this.onChangeValue}
/>
<span class="form-check-label font-weight-bold">
Movies
</span>
</div>
<div class="form-check">
<input
class="form-check-input"
type="radio"
value="tv"
name="tvshow"
checked={this.state.selectedOption === "tv"}
onChange={this.onChangeValue}
/>
<span class="form-check-label font-weight-bold">
TV Shows
</span>
</div>
</div>
</form>
</div>
</div>
</div>
{/* search results */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<div className="text-center">
<div className="font-weight-lighter h2"> Search Results </div>
</div>
</div>
</>
)
}
}
export default SearchBox;
the array of result is in result variable of handleSubmit(). how can I use useState to store my result var and then show it on scrren.
if you don't understand how i'm talking about using this hook i've attached a file which does similar thing.
landingComponent.js
import React, { useEffect, useState } from 'react';
import {API_URL, API_KEY, IMAGE_URL} from '../../config/keys';
import { Row } from 'reactstrap';
import MainImage from './MainImage';
import GridCard from './GridCard';
import { Link } from 'react-router-dom';
function LandingPage() {
const [Movies, setMovies] = useState([]);
const [CurrentPage, setCurrentPage] = useState(0);
useEffect( () => {
const endpoint = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=1`;
fetchMovies(endpoint);
}, []);
const fetchMovies = (path) => {
fetch(path)
.then(response => response.json())
.then(response => {
console.log(response);
setMovies([...Movies, ...response.results]);
setCurrentPage(response.page);
})
}
const handleClick = () => {
const endpoint = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=${CurrentPage + 1}`
fetchMovies(endpoint);
}
return (
<div style={{ width: '100%', margin: 0 }} >
<div style={{ width: '95%', margin: '1rem auto' }}>
{/* Breadcrumbs */}
<nav aria-label="breadcrumb">
<ol className="breadcrumb">
<li className="breadcrumb-item"><Link to='/'>Home</Link></li>
<li className="breadcrumb-item active" aria-current="page">Movies</li>
</ol>
</nav>
<div className="font-weight-bold h2"> Latest Movies </div>
<hr style={{borderColor:'black'}}/>
<Row>
{Movies && Movies.map((movie, index) => (
<React.Fragment key={index}>
<GridCard
image={movie.poster_path && `${IMAGE_URL}w500${movie.poster_path}`}
movieId={movie.id} movieTitle={movie.title} name={movie.original_title}
/>
</React.Fragment>
))}
</Row>
<br />
<div className="text-center">
<button className="btn btn-primary" onClick={handleClick}> Load More </button>
</div>
</div>
</div>
)
}
export default LandingPage
I want to use same way in SearchComponent.js. I tried so many thing but none worked. Help is appreciated.
React has two ways of using components :
Class components
Declared this way : class ComponentName extends Component {
then your state is managed using : this.setState()
Function components
Declared just as a function as your second example
There you can use hooks and the useState()
So if you're not planning to re-write all your component you'll have to use this.setState()
When you fetch the data you need to store it in the state again creating results:[] property in the state.
handleSubmit = (e) => {
e.preventDefault();
var data = this.state.searchQuery;
const url = `${API_URL}search/${this.state.selectedOption}?api_key=${API_KEY}&language=en-US&query=${encodeURI(data)}&page=1&include_adult=false`;
fetch(url)
.then(response => response.json())
.then(response => {
console.log(response);
const result = response.results;
this.setState({results:result}); //Here you store the state.
});
}
Now you will have to show it in the results JSX block.
{/* search results */}
<div style={{ width: '95%', margin: '1rem auto' }}>
<div className="text-center">
<div className="font-weight-lighter h2"> Search Results </div>
<div className="results">
<ul>
{this.state.results.length && this.state.results.map(item => { return (
<li> {item} </li>
) })}
</ul>
</div>
</div>
</div>

How to create a resposnive search filter with React

I have a DogCardsDisplayed React Component which maps through an Array of objects that displays the data in cards to the page. My SearchBar Component searches through the the array and filters the list in the console based on user search. How do I apply that to the actual cards so that the cards themselves are filtered on the page.
const dogData = [
{
id: 1,
name: "Hollie",
breed: "Doberman",
age: 3,
weight: 20,
height: 150,
img: "https://placedog.net/500/200?id=61",
},
{
id: 2,
name: "Charles",
breed: "Beagle",
age: 4,
weight: 20,
height: 150,
img: "https://placedog.net/500/200?id=100",
}
import DogCardsDisplayed from "./DogCardsDisplayed";
import dogData from "./dogData";
import { Nav, Navbar, Button } from "react-bootstrap";
function SearchBar() {
const onSub = (e) => {
let substring = e.target.value;
let filteredData = dogData.filter(
(item) =>
item.name.toLowerCase().includes(substring.toLowerCase()) ||
item.breed.toLowerCase().includes(substring.toLowerCase())
);
console.log(filteredData);
};
return (
<Navbar className="d-block">
<form>
<input
onChange={onSub}
className="search-input"
placeholder="Search"
name="search"
></input>
<Button
variant="outline-light"
className="search-button"
type="submit"
>
Search
</Button>
</form>
</Navbar>
);
}
function DogCardsDisplayed() {
return dogData.map((item) => (
<Card key={item.id} className="card">
<div>
<Card.Img variant="top" src={item.img} />
<Card.Body>
<Card.Title>{item.name}</Card.Title>
<div className="d-flex">
<div><b>Breed:</b> {item.breed}</div>
<div><b>Age:</b> {item.age}</div>
<div><b>Weight:</b> {item.weight}lb</div>
<div><b>Height:</b> {item.height}in</div>
</div>
</Card.Body>
</div>
</Card>
));
}
function SearchPage() {
return (
<>
<SearchBar />
<div className="d-flex flex-wrap sp-body">
<DogCardsDisplayed />
</div>
</>
);
}
export default SearchPage;
You can achieve this by storing the filter in the state (useState)[1] and then using useMemo [2] to return the filtered data based on changes to the state filter and the dogData.
function SearchBar({ onSearch }) {
const onSub = (e) => {
onSearch(e.target.value.toLowerCase());
};
return (
<Navbar className="d-block">
<form>
<input
onChange={onSub}
className="search-input"
placeholder="Search"
name="search"
/>
</form>
</Navbar>
);
}
function DogCardsDisplayed({ dogData }) {
return dogData.map((item) => (
<Card key={item.id} className="card">
<div>
<Card.Img variant="top" src={item.img} />
<Card.Body>
<Card.Title>{item.name}</Card.Title>
<div className="d-flex">
<div><b>Breed:</b> {item.breed}</div>
<div><b>Age:</b> {item.age}</div>
<div><b>Weight:</b> {item.weight}lb</div>
<div><b>Height:</b> {item.height}in</div>
</div>
</Card.Body>
</div>
</Card>
));
}
function SearchPage() {
const [filter, setFilter] = React.useState("");
const filteredData = React.useMemo(() => {
if (filter == "") return dogData;
return dogData.filter(
(item) =>
item.name.toLowerCase().includes(filter) ||
item.breed.toLowerCase().includes(filter)
);
}, [dogData, filter]);
return (
<>
<SearchBar onSearch={(searchTerm) => setFilter(searchTerm)}/>
<div className="d-flex flex-wrap sp-body">
<DogCardsDisplayed dogData={filteredData} />
</div>
</>
);
}
References:
[1] useState hook - https://reactjs.org/docs/hooks-state.html
[2] useMemo hook - https://reactjs.org/docs/hooks-reference.html#usememo

Resources