How to make React search component - javascript

I am doing my first Ecommerce MERN stack project but i donot know how to make search component that will take the search input and return array of matching products

If you want to make it fast you can get a graphic library like Material UI : https://mui.com/
Like this you can use pre-created components like this one that have autocomplete: https://mui.com/components/autocomplete/#search-input
You don't tell a lot of details about your case but to use it, you have to configure a route that returns you a list of the researched input everytime there is a change in this said input (configure the onChange function of the input section to request the list everytime there is a change).

To add a search:
Render your search bar component in the app
Add your HTML elements
Add a list of posts
Filter the list based on your search query
Adding immediate search or “search as you type”
Adding SPA navigation with React Router
“Search as you type”, SPA navigation and accessibility concerns
Testing your component with React Testing Library
Conclusion
If you want to know more precisely, you can view and understand the related material at the following URL.
https://www.emgoto.com/react-search-bar/
Best regards.

Using bootstrap:
import React, { useState } from "react";
import { useRouter } from "next/router";
const Search = () => {
const [location, setLocation] = useState("");
const [guests, setGuests] = useState("");
const [category, setCategory] = useState("");
const router = useRouter();
const submitHandler = (e) => {
e.preventDefault();
if (location.trim()) {
router.push(
// this will push it to home page with query params
// in home pag, based on these query params, you should be fetching data
`/?location=${location}&guests=${guests}&category=${category}`
);
} else {
router.push("/");
}
};
return (
// using bootstrap classes
<div className="container container-fluid">
<div className="row wrapper">
<div className="col-10 col-lg-5">
<form className="shadow-lg" onSubmit={submitHandler}>
<h2 className="mb-3">Search Rooms</h2>
<div className="form-group">
<label htmlFor="location_field">Location</label>
<input
type="text"
className="form-control"
id="location_field"
placeholder="new york"
value={location}
onChange={(e) => setLocation(e.target.value)}
/>
</div>
<div className="form-group">
<label htmlFor="guest_field">No. of Guests</label>
{/* The <select> element is used to create a drop-down list. */}
<select
className="form-control"
id="guest_field"
value={guests}
onChange={(e) => setGuests(e.target.value)}
>
{/* upto 6 guests */}
{[1, 2, 3, 4, 5, 6].map((num) => (
// The <option> tags inside the <select> element define the available options in the drop-down list.
<option key={num} value={num}>
{num}
</option>
))}
</select>
</div>
<div className="form-group">
<label htmlFor="room_type_field">Room Type</label>
<select
className="form-control"
id="room_type_field"
value={category}
onChange={(e) => setCategory(e.target.value)}
>
{["King", "Single", "Twins"].map((category) => (
<option key={category} value={category}>
{category}
</option>
))}
</select>
</div>
<button type="submit" className="btn btn-block py-2">
Search
</button>
</form>
</div>
</div>
</div>
);
};
export default Search;

Related

Updating default values of a React-Hook-Form, does not populate filelds with useFieldArray

This problem has consumed a lot of time trying to figure out what is wrong. I have a React-Hook-Form (v6.14.1) that needs to populate dynamic data, based on the component state.
On the initial load, everything works fine. If I change the state all updated data are displaying fine, except the dynamic data.
Here is a codesandbox link. If it does not render due to a library error, just hit the preview refresh button.
The goal is that the WAN 1 tab, on initial load displays the dynamic fields (WAN 1 VLAN-1) and WAN2 does not have dynamic fields to display. Hitting the Update Config button, WAN1 should not have dynamic fields to display and WAN2 should display one (WAN 2 VLAN-1). The problem is that WAN2 does not display it.
I have searched for similar questions, but all of them were about the values of the populated fields and not about displaying the fields themselves. I have used the reset method of react-hook-form and the defaltValue for each dynamic field as react-hook-form documentation suggests.
On App.js I have the state, a button that updates the state, and the Form component which has the state as property.
const [configdata, setConfigdata] = useState(config);
return (
<div className="App">
<UpdateConfig onClick={() => setConfigdata(configUpdated)} />
<Form
formData={configdata}
handleFormData={(data) => console.log(data)}
/>
</div>
);
}
On Form.js there is a Rect-hook-form FormProvider and the WanFields component that dynamically populates form fields.
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit((data) =>
props.handleFormData(data)
)}
>
<Tab.Content>
{props.formData?.intfs?.length &&
props.formData?.intfs.map((intf, index) => (
<Tab.Pane key={index} eventKey={`wan${index}-tab`}>
<WanFields
key={`wan${index}-fields`}
intfNo={index}
portTypeOptions={props.portTypeOptions}
data={intf}
/>
</Tab.Pane>
))}
</Tab.Content>
</form>
</FormProvider>
Every time the props.formData update, there is a useEffect that reset the forms' default data.
const methods = useForm({ defaultValues: props.formData });
useEffect(() => {
methods.reset(props.formData);
}, [props.formData]);
In WanFields.js, there are all the form fields, and the useFieldArray method, that will populate the dynamic fields based on the forms' default values and a watch field value (watchIntfType ).
const methods = useFormContext();
const { errors, control, watch, register } = methods;
const { fields, append, remove } = useFieldArray({
control,
keyName: "fieldid",
name: `intfs[${intfNo}].subIntfs`
});
const watchIntfStatus = watch(`intfs[${intfNo}].enabledStatus`);
const watchIntfType = watch(`intfs[${intfNo}].enabled`);
Dynamic fields are populated as follows
{watchIntfType?.value >= "2" && (
<>
<div className="form-group">
<div className="btn btn-success" onClick={append}>
Add
</div>
</div>
<div id={`accordion-${intfNo}`}>
<Accordion>
{console.log("FIELDS", fields)}
// This is where the problem starts. fields are empty after updating data
{fields.map((field, index) => {
return (
<Card key={field.fieldid}>
<Accordion.Toggle
as={Card.Header}
variant="link"
eventKey={`${index}`}
style={{ cursor: "pointer" }}
>
<h4>
WAN {parseInt(intfNo) + 1}{" "}
<span style={{ margin: "0px 5px" }}>
<i className="fas fa-angle-right"></i>
</span>{" "}
VLAN-{index + 1}
</h4>
<div className="card-header-action">
<button
type="button"
className="btn btn-danger"
onClick={() => remove(index)}
>
Remove
</button>
</div>
</Accordion.Toggle>
<Accordion.Collapse eventKey={`${index}`}>
<Card.Body>
<div className="form-row">
<div className="form-group col-12 col-md-6">
<label>IP</label>
<input
type="text"
className="form-control"
name={`intfs[${intfNo}].subIntfs[${index}].ipAddress`}
defaultValue={field?.ipAddress}
ref={register()}
/>
</div>
<div className="form-group col-12 col-md-6">
<label>Subnet</label>
<input
type="number"
className="form-control"
min="0"
max="30"
name={`intfs[${intfNo}].subIntfs[${index}].subnet`}
defaultValue={field?.subnet}
ref={register()}
/>
</div>
</div>
</Card.Body>
</Accordion.Collapse>
</Card>
);
})}
</Accordion>
</div>
</>
)}
The problem is that when the state updates, form default values are updated, but the method useFieldArray attribute fields are not updated and stay as an empty array. I really cannot understand, what I am doing wrong. Any help will be much appreciated.
I don't know if is a correct method but i have resolv this probleme with method reset in a useEffect.
https://react-hook-form.com/api/useform/reset
defaultValues:
{
acvDesignOffice: generateRSEnv.acvDesignOffice,
earthQuakeZone: generateRSEnv.earthQuakeZone,
buildings: generateRSEnv.buildings,
},
useEffect(() => {
reset({
acvDesignOffice: generateRSEnv.acvDesignOffice,
earthQuakeZone: generateRSEnv.earthQuakeZone,
buildings: generateRSEnv.buildings,
});
}, [generateRSEnv]);

React Button Click should change the display value of div

I got a Navbar which has a button do change the display value of a login form. The Login form and the Login form is a diffrent file, the navbar is a diffrent file and the homepage where it should be display is a diffrent file. Those are the minimal variants of each so that you got some got to understand my problem in detail:
Homepage:
const HomePage = () => {
return (
<div>
<Navbar />
<Login />
<div id="content">
</div>
</div>
);
}
Navbar:
const Navbar= () => {
const showLogin = () => {
document.getElementById('Login').style.display='block';
}
return (
<div id="Navbar">
<NavLink activeClassName="active" to='/'><img src={logo}/></NavLink>
<ul>
...
</ul>
<ul>
<button onClick={showLogin}>Anmelden</button>
</ul>
</div>
);
}
Login-Form:
const Login = () => {
return (
<div id="Login">
<form>
<label>Anmelden</label>
<label for="username">Nutzername</label>
<input name="username" type="text"></input>
<label for="pw">Passwort</label>
<input name="pw" type="password"></input>
<button type="submit">Login</button>
</form>
</div>
);
}
Is there a way to achieve this, or would my easiest option be to include the Login source code into the Navbar source code?
You do not need to move your Login component inside Navbar. Keep it as it is.
You can use useState and Props to switch css classes to show/hide your Login component. I have created very simple solution for you in this CodeSandbox.
Steps:
Create two CSS classes hidden and block
In your Login component add a boolean prop which switches class hidden to block if true.
Create a prop for onClick in the Login component.
Create a useState inside your Homepage which holds a boolean value. That boolean value pass it to the Login page prop and then use onClick prop from Navbar to switch that boolean state
Yes, depending on your CSS system this is easily achievable just by using that.
The React solution is using refs.
The easy way is to create a ref in the parent component and then pass it down as a prop to both components:
In Homepage (i.e. parent), create a ref like so loginRef = useRef(); then pass it down as a prop to the 2 children.
In Login-Form.js you assign that prop on the div with id Login like so <div id='Login' ref={props.loginRef}>
Then in Navbar.js you can use that prop to change its display value like so const showLogin = () => {props.loginRef.current.style.display='block';}
NB: This is a fast and easy way, not best practice but it gets the work done. The best-practice here is to use forwardRef and - super advanced - useImperativeHandle. Take your time and go through the documentation when you're ready.
Login page will show "it is not active" first because active is set to false.but once you click on submit button it will show "it is active"
HomePage
const HomePage = () => {
const[active,setActive]=useState(false);
return (
<div>
<Navbar activesetter={setActive} />
<Login activestatus={active} />
<div id="content">
</div>
</div>
);
}
Login
const Login = (props) => {
return(
<div>
<div>
{props.activestatus ? "it is active" : "it is not active" }
</div>
</div>
);
}
Navbar
const Navbar = (props) => {
const handleSubmit=(e)=> {
e.preventDefault();
props.activesetter(true);
}
return(
<div>
<form onSubmit={handleSubmit}>
<button type="submit">Login</button>
</form>
</div>
);
}

How do i pass data values to modals using react hooks?

I am working on using modals to accept form inputs on a react project
here is the plan component
plan/index.js
import React, { useState } from "react";
import Pay from '../Pay';
const Plan = () => {
const [payModal, setPayModal] = useState(false);
const [planMode, setPlanMode] = useState(true);
return (
<main class="main">
{payModal && <Pay setOpenPayModal={setPayModal} />}
<div class="plan">
<div>
<a class="plans" onClick={() => {setPayModal(true);}}>plan A</a>
<div class="plan">
<span class="plan">{!planMode ? "$19.99" : "$9.99"}</span>
<span class="plan">{!planMode ? "month" : "year"}</span>
</div>
</div>
<div>
<a class="plans" onClick={() => {setPayModal(true);}}>plan B</a>
<div class="plan">
<span class="plan">{!planMode ? "$29.99" : "$19.99"}</span>
<span class="plan">{!planMode ? "month" : "year"}</span>
</div>
</div>
</div>
</main>
);
};
export default Plan;
as you can see on the line {payModal && <Pay setOpenPayModal={setPayModal} />} where i call the pay modal component from the plan component and it opens up the modal
here is the pay component which is the modal component
pay/index.js
import React, { useState } from "react";
function Pay({ setOpenPayModal }) {
const [billingDetails, setBillingDetails] = useState({
price: "",
: "",
});
return (
<div class="modal">
<div class="modal">
<div class="close">
<button
onClick={() => {
setOpenPayModal(false);
}}
>
</button>
</div>
<div class="modal">
<form class="form" onSubmit={handleSubmit}>
<fieldset class="form">
<Field
label="Price"
id="price"
type="text"
value={billingDetails.price}
onChange={(e) => {
setBillingDetails({ ...billingDetails, price: e.target.value });
}}
/>
<Field
label="Frequency"
id="frequency"
type="text"
value={billingDetails.frequency}
onChange={(e) => {
setBillingDetails({ ...billingDetails, frequency: e.target.value });
}}
/>
</fieldset>
<button class="pay" onClick={handleSubmitPay}>
Pay
</button>
</form>
</div>
</div>
</div>
);
}
export default Pay;
The issue is I want to be able to pass the values price and frequency from the plan component to the pay modal component
for example for plan A is price="$19.99" and frequency="year", so based on the button clicked on the plan component page, those values get passed to the pay modal component in a dynamic way
how do I achieve this using react hooks?
You can use contexts to pass, but in this case I don't think it's the best option. What I really recommend is passing the state variable through the props.
{payModal && <Pay setOpenPayModal={setPayModal} price={price} frequency={frequency} />}
I usually use the Context (useContext) when I need values and various components, for example:
I need to save the user id that is logged in to various components, and instead of getting it from the server every time I need it, I keep it saved in my context that I created, so I can make the call only once.
Documentation-> https://pt-br.reactjs.org/docs/context.html

How to properly search in a list in ReactJS

I am trying to set a simple search operation in a user interface as shown below:
I have a total of 70 react-strap cards and each card contain a vessel with name, type and an image. I would like to search the name of the vessel and have the card related to that vessel to pop-up. All my images are currently contained inside the external database Contentful. Below the fields of interests:
The problem is that I don't know how to write a search function that locate a specific value of a list.
Below the code:
SideBar.js
import React from 'react';
import Client from '../Contentful';
import SearchVessel from '../components/SearchVessel';
class Sidebar extends React.Component {
state = {
ships: [],
};
async componentDidMount() {
let response = await Client.getEntries({
content_type: 'cards'
});
const ships = response.items.map((item) => {
const {
name,
slug,
type
} = item.fields;
return {
name,
slug,
type
};
});
this.setState({
ships
});
}
getFilteredShips = () => {
if (!this.props.activeShip) {
return this.state.ships;
}
let targetShip = this.state.ships.filter(
(ship) => this.props.activeShip.name === ship.name
);
let otherShipsArray = this.state.ships.filter((ship) => this.props.activeShip.name !== ship.name);
return targetShip.concat(otherShipsArray);
};
render() {
return (
<div className="map-sidebar">
{this.props.activeShipTypes}
<SearchVessel />
<pre>
{this.getFilteredShips().map((ship) => {
console.log(ship);
return (
<Card className="mb-2">
<CardImg />
<CardBody>
<div className="row">
<img
className="image-sizing-primary"
src={ship.companylogo.fields.file.url}
alt="shipImage"
/>
</div>
<div>
<img
className="image-sizing-secondary"
src={ship.images.fields.file.url}
alt="shipImage"
/>
</div>
<CardTitle>
<h3 className="thick">{ship.name}</h3>
</CardTitle>
<CardSubtitle>{ship.type}</CardSubtitle>
<CardText>
<br />
<h6>Project Details</h6>
<p>For a description of the project view the specification included</p>
</CardText>
<Row style={{ marginTop: '20px' }}>
<div className="buttoncontainer">
<div className="btn btn-cards">
<a
className="buttonLink"
download
href={ship.projectnotes.fields.file.url}
>
Project Notes
</a>
</div>
<div className="btn btn-cards">
<a className="buttonLink" href={ship.abstract.fields.file.url}>
Abstract
</a>
</div>
</div>
</Row>
</CardBody>
</Card>
);
})}
</pre>
</div>
);
}
}
export default Sidebar;
VesselSearch.js
import React, { Component } from 'react';
export default class SearchVessel extends Component {
render() {
const { value, handleSubmit, handleChange } = this.props;
return (
<React.Fragment>
<div className="container">
<div className="row">
<div className="col-10 mx-auto col-md-8 mt-5 text-center">
<h4 className="text-slanted text-capitalize">Search for Vessel</h4>
<form className="mt-4" onSubmit={handleSubmit}>
<label htmlFor="search" className="text-capitalize">
type vessel separated by comma
</label>
<div className="input-group">
<input
type="text"
name="search"
placeholder="Type name of vessel here"
className="form-control"
value={value}
onChange={handleChange}
/>
<div className="input-group-append">
<button type="submit" className="input-group-text bg-primary text-white">
<i className="fas fa-search" />
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</React.Fragment>
);
}
}
What I have done so far:
1) I tried different combination with the filter function and I think I am close. The problem is that when I operate the search nothing happens and in order to find the card of the vessel I want, I have to scroll down until I find it.
I am running out of ideas and if you see something I didn't catch point me in the right direction for solving this issue.
You're close! I would add a field to your state called 'searchText' and then create a method to filter based on that searchText state item.
getFilteredShips = () => this.state.ships.filter(s => s.name.includes(this.state.searchText)
Then just map over those values to render the cards that match the search text. The cards will update each time the searchText value updates.
this.getFilteredShips().map(ship => ..........
React is famous for re-usable component. You will have all the data of these vessels in an array. You will loop through the array and render the items with card component.And when you search for the specific card you want that vessel to pop out on top.
There are two ways to do it:
You have to run through the array, find the index of that vessel and do whatever it takes to manipulate your array and to make that item at top and re-render your list.
Alternatively render one more component on top of your vessel list as user clicks the search button. You just have to find the item index and render it. This way you don't have to deal with array manipulation. It doesn't matter if you have 80 or 1000 cards.
Please checkout official documentation for array methods, for array slicing and splice.
Hope this is what you are looking for. If you need further help, comment please.

How do I replace the current component content with other component content in React

Hey guys I know the title is a bit confusing , but im sure here you will understand better what i need. So im trying to build a social media app like instagram and at the moment im building the login component but im stuck since im still learning how to use react.So here I have my login component:
function LoginMenu (){
const [email, setEmail] = useState(null);
const [password, setPassword] = useState(null);
return (
<div style = {styles2}>
<form className="form-signin" style = {styles}>
<div className="form-label-group" >
<label htmlFor="inputEmail">
Email address
<input
type="email"
id="inputEmail"
className="form-control"
placeholder="Email address"
required
autoFocus
onChange = {event => setEmail(event.target.value)}
></input>
</label>
</div>
<div className="form-label-group" >
<label htmlFor="inputPassword">
Password
<input
type="password"
id="inputPassword"
className="form-control"
placeholder="Password"
required
onChange = {event => setPassword(event.target.value)}
></input>
</label>
</div>
<button
className="btn btn-lg btn-primary btn-block"
onClick = {() => <MainApp/>}
type="submit"
>
Sign in
</button>
</form>
</div>
);
}
all I want to do is , when i click the button , the MainApp components content to take over the page and the login menu to be gone, just like it happens in every site with login screen
I advise you to learn about Router in ReactJs which enable the navigation between multiple pages.
Else you can define a state property displayMain to update your render.
For example :
let [displayMain, setDisplayMain] = useState(false); // initial value is false
if(displayMain){
return <MainComponent/>; // Your main component to render
}
else{
// your Login Component to render
return <button onClick=>{()=>setDisplayMain(true)}/>; // update displayMain
}
But I insist that you should use Router to implement an efficient solution.

Categories

Resources