how can i pass props from a select component to my app js
here my code:
const HandleSelect = ()=> {
const [selectedOption, setSelectedOption] = useState();
return(
<div className="App">
<Select
defaultValue={selectedOption}
onChange={setSelectedOption}
options={options}
/>
</div>
)
}
export default HandleSelect;
i've tried with
const Select = (props) => {
const {
defaultValue, onChange, options
} = props
console.log(onChange);
return (
<div>
</div>
)
}
export default Select
but in the app they return undefined when i try with console.log()
sorry for my english, i'm French.
Thanks for your help.
Destructure your props like this
const Select = ({defaultValue, onChange, options}) => {
return (
<div>
<p>{defaultValue}</p>
<p>{onChange}</p>
<p>{options}</p>
</div>
)
}
export default Select
or you could also do it this way
const Select = (props) => {
return (
<div>
<p>{props.defaultValue}</p>
<p>{props.onChange}</p>
<p>{props.options}</p>
</div>
)
}
export default Select
Here my app initial where i want to use the select, it's a lot of code, sorry for that !
import React, { useState, useEffect } from 'react';
import '../App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { FiPlusCircle } from 'react-icons/fi';
import { InputGroup } from 'react-bootstrap';
import { FormControl } from 'react-bootstrap';
import { Button } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import ImageUpload from 'image-upload-react';
import { doc, setDoc, getDoc } from 'firebase/firestore';
import { decode as base64_decode, encode as base64_encode } from 'base-64';
import Select from './select';
// Initialize Cloud Firestore through Firebase
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
const object1 = { title: 'Entrecôte', category: 'Viande', describe: 'Ici la description' };
const firebaseApp = initializeApp({
//my config
});
const db = getFirestore();
const Initial = (props) => {
const [ data, setData ] = useState(object1);
const [ loadata, setLoadata ] = useState();
const [ imageSrc, setImageSrc ] = useState(); // form image source
const [ encoded, setEncoded ] = useState(); // Value
const [ title, setTitle ] = useState(''); // title
const [ category, setCat ] = useState(''); // category
const [ describe, setDesc ] = useState(''); // description
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => setData(data);
// set url data
const handleImageSelect = (e) => {
setImageSrc(URL.createObjectURL(e.target.files[0]));
};
useEffect(() => {
if (data) {
//form only
setTitle(data.title);
setCat(data.category);
setDesc(data.describe);
addTodo(title, category, describe, encoded); // great !
}
}, []);
// write values to firebase database
const addTodo = async () => {
const response = await fetch(imageSrc);
const blob = await response.blob();
var reader = new FileReader();
reader.onload = () => {
setEncoded(reader.result);
};
reader.readAsDataURL(blob);
if (encoded && data) {
await setDoc(doc(db, 'User', 'user000'), {
category: category,
title: title,
describe: describe,
image: encoded
});
}
};
function getScrollHeight(elm) {
var savedValue = elm.value;
elm.value = '';
elm._baseScrollHeight = elm.scrollHeight;
elm.value = savedValue;
}
function onExpandableTextareaInput({ target: elm }) {
// make sure the input event originated from a textarea and it's desired to be auto-expandable
if (!elm.classList.contains('autoExpand') || !elm.nodeName === 'TEXTAREA') return;
var minRows = elm.getAttribute('data-min-rows') | 0,
rows;
!elm._baseScrollHeight && getScrollHeight(elm);
elm.rows = minRows;
rows = Math.ceil((elm.scrollHeight - elm._baseScrollHeight) / 16);
elm.rows = minRows + rows;
}
// global delegated event listener
document.addEventListener('input', onExpandableTextareaInput);
// Ici je veux retourner le produit (lecture)
const getUser = async () => {
const list = [];
const docRef = doc(db, 'User', 'user000');
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
list.push(docSnap.data());
const newlist = list[0];
setLoadata(newlist);
} else {
console.log('No such document!');
}
};
useEffect(() => {
getUser();
}, []);
// console.log('propsinou', props);
return (
<div className="container-fluid css-selector m-0 p-0" style={{ height: '100vh' }}>
<div className="row m-0 p-0">
<div className="col-12 row m-0">
{/* column card */}
<div className="col-md-1 cardColor mt-5 text-center text-white size">
<h4 className="mt-3 mb-4"> Bienvenue </h4>
<div className="round">
<FiPlusCircle size={40} />
</div>
<p>Ajouter menu</p>
</div>
<div className="col-11 row space m-0 justify-content-center">
<h1 className="m-0 text-white text-center mt-5">Bienvenue sur votre Dashboard</h1>
<div className="col-10 row m-0">
<h4 className="m-0 text-white p-3">Étape 1 : ajouter du contenu </h4>
{/* First card */}
<div className="col-3 text-center card">
{loadata ? (
<div class="container">
<h3 className="m-0 text-success p-3">Titre</h3>
<InputGroup className="mb-3">
<InputGroup.Text id="basic-addon1">📌</InputGroup.Text>
<FormControl
placeholder={loadata.title}
aria-label="Titre du menu"
aria-describedby="basic-addon1"
{...register('title')} // onChange={e=>
// this.setState({ val: e.target.value })}
/>
</InputGroup>
<h3 className="m-0 text-success">Catégorie</h3>
<InputGroup className="mb-3 mt-3">
<InputGroup.Text id="basic-addon1">📜</InputGroup.Text>
<FormControl
placeholder={loadata.category}
aria-label="Username"
aria-describedby="basic-addon1"
{...register('category')} // onChange={e=>
// this.setState({ val: e.target.value })}
/>
</InputGroup>
</div>
) : (
<div class="container">
<h3 className="m-0 text-success p-3">Titre</h3>
<InputGroup className="mb-3">
<InputGroup.Text id="basic-addon1">📌</InputGroup.Text>
<FormControl
placeholder="titrent"
aria-label="Titre du menu"
aria-describedby="basic-addon1"
{...register('title')} // onChange={e=>
// this.setState({ val: e.target.value })}
/>
</InputGroup>
<h3 className="m-0 text-success">Catégorie</h3>
<InputGroup className="mb-3 mt-3">
<InputGroup.Text id="basic-addon1">📜</InputGroup.Text>
<FormControl
placeholder="Username"
aria-label="Username"
aria-describedby="basic-addon1"
{...register('category')} // onChange={e=>
// this.setState({ val: e.target.value })}
/>
</InputGroup>
</div>
)}
</div>
<div className="col-3 card offset-1">
<div className="container">
<h3 className="m-0 text-success p-3 text-center">Description</h3>
{loadata ? (
<textarea
className="autoExpand"
rows="3"
data-min-rows="3"
placeholder={loadata.describe}
{...register('describe')}
autoFocus
/>
) : (
<textarea
className="autoExpand"
rows="3"
data-min-rows="3"
placeholder={'Entrez la description'}
{...register('describe')}
autoFocus
/>
)}
</div>
</div>
<div className="col-3 offset-1 p-absolute">
<ImageUpload
className="border-r"
handleImageSelect={handleImageSelect}
imageSrc={imageSrc}
setImageSrc={setImageSrc}
style={{
width: '100%',
height: '100%',
background: 'gold'
}}
/>
</div>
</div>
<div className="col-10 row m-0">
<div className="col-3"></div>
<h4 className="m-0 text-white p-3 mt-5">Étape 2 : Choisir la catégorie & valider </h4>
{/* First card */}
<div className="col-3 text-center card">
<div class="container">
<h3 className="m-0 text-success p-3">Catégorie</h3>
<Select />
</div>
</div>
<div className="col-3 offset-1 text-center card">
<div class="container">
<h3 className="m-0 text-success p-3">Valider</h3>
{/* <Button /> */}
<Button variant="success" onClick={handleSubmit(onSubmit)}>
Envoyer les données
</Button>{' '}
</div>
</div>
</div>
</div>
{/* Boutton Select*/}
</div>
</div>
</div>
);
};
export default Initial;
Related
I'm working on a Auth with MERN , at the client side i'm using contextAPI ,useReducerhook to manage the user.
I have also created a custom hook ( useLogin ) for handling the login criteria as shown below .It takes the userCredentials whoch is a object of (email password) from the login form .I have also incorporated the reacy-hook form to handle validation of the input fields
import axios from "axios";
import { BASE_API_URL } from "../utils/BaseUrl";
import { useAuthContext } from "./useAuthContext";
export const useLogin = () => {
const [isError, setIsError] = useState(null)
const [isLoading, setIsLoading] = useState(false)
const [IsSuccess, setIsSucces] = useState(false)
const { dispatch } = useAuthContext()
const loginUser = async (userDetails) => {
setIsError(null)
setIsLoading(true)
try {
const response = await axios.post(`${BASE_API_URL}/api/auth/login`, {
userDetails: userDetails
})
//get json data
const jsonData = await response.json()
//if there is a error in the response
if (!response.ok) {
setIsLoading(false)
setIsError(jsonData.error)
}
//if it works correctly
if (response.ok) {
//save user to local storage
localStorage.setItem("userData", JSON.stringify(jsonData));
localStorage.setItem("accessToken", jsonData.data?.accessToken)
//update the auth context
dispatch({ type: "LOGIN", paylaod: jsonData })
setIsLoading(false)
setIsSucces(true)
}
} catch (err) {
setIsError(err.message)
setIsLoading(false)
}
}
return { isError, isLoading, IsSuccess, loginUser }
}
Then i re use it at my login page as shown here
import ScaleLoader from "react-spinners/ScaleLoader";
import Logo from "../../assets/images/Logo.png";
import Footer from "../Footer/index";
import { Link } from "react-router-dom";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import Input from "#material-ui/core/Input";
import Swal from "sweetalert2";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faEye, faEyeSlash } from "#fortawesome/free-solid-svg-icons";
import { useLogin } from "../../hooks/useLogin";
const Login = () => {
const [userDetails, setUserDetails] = useState("");
const [passwordShown, setPasswordShown] = useState(false);
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const navigate = useNavigate();
const { loginUser } = useLogin()
// Password toggle handler
const togglePassword = () => {
setPasswordShown(!passwordShown);
};
//handleSubmit
const onSubmit = async () => {
await loginUser(userDetails)
navigate("/user/dashboard");
setUserDetails('')
};
return (
<>
{/* <div className="d-flex justify-content-center loader">
<ScaleLoader
className="d-flex justify-content-center"
color="#FFC247"
speedMultiplier={4}
/>
</div> */}
<div className="login-page">
<div className="container">
<div className="row">
<div className="col-lg-6 offset-lg-3 col-md-12 offset-md-1 col-sm-12 offset-sm-1 col-xs-6 offset-xs-3">
<div className="login-form">
<div className="login-form-header d-flex flex-column justify-content-center flex-column align-items-center">
<img src={Logo} alt="" />
<h4>We missed you , glad to see you back</h4>
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label htmlFor="email1">Email address</label>
<input
type="email"
className={`form-control py-2 my-2 ${errors.email ? "input-error" : ""
}`}
id="email"
name="email"
placeholder="Enter email"
{...register("email", {
required: "Email is required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "Invalid email address",
},
})}
/>
{errors.email && (
<span className="errorMsg">{errors.email.message}</span>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<div className="input-group">
<Input
type={passwordShown ? "text" : "password"}
className="form-control py-2 my-2"
id="password"
name="password"
placeholder="Enter password"
{...register("password", {
required: "Password is required",
minLength: {
value: 8,
message: "Password must be at least 8 characters",
},
pattern: {
value:
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##$%^&*])/,
message:
"Password must contain at least one lowercase letter, one uppercase letter, one number and one special character",
},
})}
/>
<span
style={{ height: "50px", marginTop: "8px" }}
className="input-group-text"
onClick={togglePassword}
>
{passwordShown ? (
<FontAwesomeIcon icon={faEyeSlash} />
) : (
<FontAwesomeIcon icon={faEye} />
)}
</span>
</div>
{errors.password && (
<span className="errorMsg">
{errors.password.message}
</span>
)}
</div>
<div className="submit-button d-flex align-items-center justify-content-between mt-5">
<button className="btn-home px-5 rounded">Login</button>
<div className="forget-password">
<Link
to="/user/forgetpassword"
className="text-decoration-none text-link"
>
Forget my password
</Link>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div className="footer-home">
<Footer />
</div>
</div>
</>
);
};
export default Login;
I want to make a form that disappears if I click outside of it.
Form Component:
const CreateTaskPopup = (props) => {
const ref = query(collection(db, "tasks"));
const mutation = useFirestoreCollectionMutation(ref);
useEffect(() => {
const closeTaskPopup = (event) => {
if (event.target.id != "addForm") {
props.setTrigger(false)
}
}
document.addEventListener('click', closeTaskPopup);
return () => document.removeEventListener('click', closeTaskPopup)
}, [])
return (props.trigger) ? (
<>
<div className="bg-darkishGrey my-4 p-1 mx-3 ml-16 cursor-pointer block"
id="addForm">
<div className="flex justify-between">
<div className="flex break-all items-center ">
<Image src={fileIcon} className="w-6 mx-2"/>
<div>
<Formik
initialValues={{
taskName: "",
taskIsDone: false,
parentId: props.parentId ? props.parentId : "",
hasChildren: false,
}}
onSubmit={(values) => {
mutation.mutate(values);
props.setTrigger(false);
}}
>
<Form>
<div className="">
<TextInput
placeholder="Type a name"
name="taskName"
type="text"
/>
</div>
</Form>
</Formik>
</div>
</div>
<div className="flex items-center">
</div>
</div>
</div>
</>
) : null
}
export default CreateTaskPopup
Text Input Component:
import { useField } from "formik";
const TextInput = ({ label, ...props}) => {
const [field, meta] = useField(props);
return (
<div>
<label id="addForm" className="text-lightestGrey text-xl block"
htmlFor={props.id || props.name}>
{label}
</label>
<input id="addForm" className="bg-darkishGrey text-xl text-almostWhite my-2
outline-none w-10/12 rounded-sm p-1 mx-3" {...field} {...props} />
{meta.touched && meta.error ? <div>{meta.error}</div>: null}
</div>
);
};
export default TextInput;
I tried giving an id to the elements inside it but it's not the best solution as it has components from the Formik library to which I can't assign an id. I don't know what would be the best solution for this problem.
I'm having a problem that I can't solve. I have a component that is currently rendering the users that are in my database, which calls CarouselUsers.jsx, so far so good, it is showing correctly.
But my goal is that after I click on one of these users that were listed, his name appears in a sidebar, which is in another component, but I am not able to do that, can you help me?
CarouselUsers.jsx
import React, { useState, useEffect } from 'react';
import * as Styled from './style.jsx';
import {
collection,
getDocs,
} from "firebase/firestore";
import { Swiper, SwiperSlide } from "swiper/react";
import { db } from '../../Data/Firebase.jsx';
import "swiper/css";
import euTeste from '../../assets/teste.jfif'
import SideBarProfile from '../../components/SideBarProfile/SideBarProfile.jsx';
export default function CarouselUsers() {
const [profile, setProfile] = useState(false)
const openProfile = () => {
setProfile(profile => !profile)
}
// USERS IN THE DB
const [users, setUsers] = useState([])
const usersCollectionRef = collection(db, "usuarios")
useEffect(() => {
const getUsers = async () => {
const data = await getDocs(usersCollectionRef);
setUsers(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
getUsers();
}, []);
// USERS IN THE DB
return (
<>
<Styled.CarouselUsers>
{/* MEMBROS CARROSEL */}
<div className="boxMembros">
<div className="titulo">
<h6>Membros</h6>
</div>
<Swiper
spaceBetween={10}
slidesPerView={3}
>
{users.map((user) => {
return (
<>
<SwiperSlide>
<div className="box"style={{ background: `linear-gradient(to bottom,rgba(0, 0, 0, 0.4) 0,rgba(0,0,0,.6) 100%),url(${euTeste})` }} onClick={openProfile} key={user.nome}>
<div className="infoBottom">
<div className="info">
{/* GET THE USERNAME */}
<h6>{user.nome}</h6>
{/* GET THE USERNAME */}
</div>
</div>
</div>
</SwiperSlide>
</>
);
})}
</Swiper>
</div>
{/* MEMBROS CARROSEL */}
</Styled.CarouselUsers>
<SideBarProfile profile={profile} openProfile={openProfile} />
</>
)
}
SideBarProfile.jsx
import React from 'react'
import { XCircle,WhatsappLogo } from "phosphor-react";
import * as Styled from './style.jsx';
export default function SideBarProfile({openProfile,profile}) {
return (
<Styled.SideBarProfile>
<div className={profile ? 'col-md-3 boxLeftWrapper open' : 'col-md-3 boxLeftWrapper close'} profile={profile}>
<div className="boxAll">
<div className="header d-flex justify-between align-items-center">
<div className="titulo">
<h1>Perfil</h1>
</div>
<div className="close">
<button onClick={openProfile}>
<XCircle/>
</button>
</div>
</div>
<div className="boxBodyUser text-left">
<div className="boxThis">
<div className="foto">
<img alt="Usuário" className='img-fluid ativo' />
</div>
<div className="nome text-center">
<h5>{/* SHOW USERNAME */}</h5>
</div>
<div className="status ativo">
<span>Ativo</span>
</div>
<div className="ministerios">
<ul className="pl-0 list-none mb-0">
<li>Teatro</li>
<li>Mídias Sociais</li>
</ul>
</div>
<div className="boxContato mt-5">
<div className="whatsapp d-flex items-center justify-center gap-2">
<WhatsappLogo/>
<span>Mensagem</span>
</div>
</div>
</div>
</div>
</div>
</div>
</Styled.SideBarProfile>
)
}
You can add an onClick event in your CarouselUsers component that grab the Inner Text in <h6>{user.nome}</h6> and pass it as props to SideBarProfile component .
like this :
CarouselUsers.jsx :
export default function CarouselUsers() {
const [profile, setProfile] = useState(false)
const [selectedUser, setSelectedUser] = useState("")
const handleClick = (event) => {
setSelectedUser(event.target.innerText);
}
// rest of your code
return (
......
{/* GET THE USERNAME */}
<h6 onClick={handleClick} >{user.nome}</h6>
{/* GET THE USERNAME */}
.... rest of your code
<SideBarProfile profile={profile} openProfile={openProfile}
setSelectedUser = {setSelectedUser} />
)
}
SideBarProfile.jsx :
export default function SideBarProfile({openProfile,profile, setSelectedUser}) {
return (
......
<div className="nome text-center">
<h5>{setSelectedUser}</h5>
</div>
....
)
I am fetching data from an API and displaying it in NearestRides component(it gives a nested object). I need to filter the data based on state or city selected in a my filtered Component, so it can display only data that matches state or city selected. I keep getting cannot read properties of undefined. I feel the issue is either Redux accessing the data or filter function, but i dont really know
this is my reduxreducer
import { actionTypes } from "../constants/action-types";
const initialState={
ridesDetails:[]
}
export const ridesReducer=(state=initialState,{type,payload})=>{
switch (type) {
case actionTypes.SET_RIDES:
return {...state, ridesDetails:payload}
case actionTypes.SELECT_RIDE_STATE:
const filterRide= state.allRides.rideDetails.filter(ride=>ride.state===payload && ride)
state= filterRide
return state;
default:
return state
}
}
this is my filterjs component
let rides = useSelector(state=>state.allRides.ridesDetails)
console.log(rides)
const dispatch = useDispatch()
const handleChange=((e)=>{
e.preventDefault()
console.log("yap this happened")
let selected_State=e.target.value
console.log(selected_State)
dispatch(selectState(selected_State))
})
function closeModal() {
setIsOpen(false);
}
return (
<Modal
isOpen={modalIsOpen}
onRequestClose={closeModal}
contentLabel="Example Modal"
className=" text-white p-5 rounded-xl mt-6 mb-0 ml-auto mr-3"
style={customStyles}
>
<div className="flex justify-between mb-2">
<h2 className="text-xl">Filters</h2>
<button className="text-3xl" onClick={closeModal}><AiOutlineCloseCircle/></button>
</div>
<hr></hr>
<form className="text-xl">
<div>
<select className=" bg-[#6a6c6d] text-white h-10 my-3 w-full" onChange={handleChange}>
<option value="">State</option>
{rides.filter(function(item){
return rides.indexOf(item.state)>-1;
}) }
{rides.map((ride,id)=>(
<option key={id} value={ride.state}>{ride.state}</option>
))}
</select>
<select className=" bg-[#6a6c6d] text-white h-10 my-3 w-full">
<option value="">City</option>
{rides.filter(function(item){
return rides.indexOf(item.city)>-1
})}
{rides.map((ride,id)=>(
<option key={id} onClick={()=>console.log(ride.city)} value={ride.city}>{ride.city}</option>
))}
</select>
</div>
</form>
</Modal>
)
}
this is my nearestRide.js component
<div>
{user &&
<div>{rides.map((ride)=>
(
<div className="flex bg-[#111] text-white my-3"key={ride.id} >
<div className="card bg-[#111] m-4 rounded-xl">
<div className="flex">
<div className="imageBox">
<img className="rounded" src={ride.map_url} alt="" />
</div>
<div className="contentBox mx-3">
<p className="my-2">{ride.state}</p>
<p className="my-2">Ride id: {ride.id}</p>
<p className="my-2">Origin Station: {ride.origin_station_code}</p>
<p className="my-2">Origin Path: {JSON.stringify(ride.station_path)}</p>
<p className="my-2">Date: {ride.date}</p>
<p className="my-2">Distance: {Math.min(...ride.station_path.map(a => Math.abs(a-user.station_code)))}</p>
</div>
</div>
</div>
</div>
)
)}</div>}
)
this is my ridesaction.js
import { actionTypes } from "../constants/action-types"
export const setRides =(allrides)=>{
return {
type:actionTypes.SET_RIDES,
payload:allrides,
}
}
export const selectCity =(city_selected)=>{
return {
type:actionTypes.SELECT_RIDE_CITY,
payload:city_selected,
}
}
export const selectState =(state_selected)=>{
return {
type:actionTypes.SELECT_RIDE_STATE,
payload:state_selected,
}
}
this is my actiontypes.js
export const actionTypes={
SET_RIDES:"SET_RIDES",
SELECT_RIDE_STATE:"SELECT_RIDE_STATE",
SELECT_RIDE_CITY:"SELECT_RIDE_STATE"
}
and this is my cominedreducer function
import { ridesReducer } from "./ridesReducer";
import { combineReducers } from "redux";
export const reducers=combineReducers({
allRides:ridesReducer,
})
I have a search bar with two input fields being combined. The first input is "desc" and the second one is "location". Every time I try to use it, they always take the "desc" input value, even though I'm filling in "location" input field too. When I try to use the second input field, the "location" input value always got undefined.
You can find my code below:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
const Home = () => {
const [jobs, setJobs] = useState([]);
const [find, setFind] = useState("");
useEffect(() => {
getJobsAPI();
// eslint-disable-next-line react-hooks/exhaustive-deps
},[])
const getJobsAPI = () => {
axios.get(`https://api.allorigins.win/raw?url=https://jobs.github.com/positions.json`)
.then((res) => {
setJobs(res.data)
})
}
const findJobsAPI = async (desc, location) => {
await axios.get(`https://api.allorigins.win/raw?url=https://jobs.github.com/positions.json?description=${desc}&location=${location}`)
.then((res) => {
setJobs(res.data)
})
}
const handleChange = (e) => {
setFind(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault();
findJobsAPI(find);
setFind("");
e.target.reset();
}
return (
<>
<div className="mx-6 my-4">
<form onSubmit={handleSubmit}>
<div className="grid grid-cols-4">
<div>
<p>Job Description</p>
<input
className="py-0.5 px-2 mr-3 border-gray-300 border-2"
placeholder="Filter by title, benefits, companies, expertise"
name="desc"
onChange={(e) => handleChange(e)}
/>
</div>
<div>
<p>Location</p>
<input
className="py-0.5 px-2 mr-3 border-gray-300 border-2"
placeholder="Filter by city, state, zip code or country"
name="location"
onChange={(e) => handleChange(e)}
/>
</div>
<div>
<input type="checkbox" />
<label className="font-semibold ml-2">Full Time Only</label>
</div>
<div>
<button type="submit" className="text-white font-semibold rounded bg-gray-500 py-2 px-3 hover:bg-gray-800 hover:transition duration-300 ease-in-out">Search</button>
</div>
</div>
</form>
<div>
<p className="text-black font-semibold text-3xl">Job List</p>
</div>
{jobs.map((item) => (
<div key={item.id} className="border-t-2">
<div className="my-3">
<div className="flex flex-wrap justify-between">
<Link to={`/positions/${item.id}`}>
<p className="text-primary-blue font-bold text-xl hover:text-blue-700 hover:transition duration-300 ease-in-out">{item.title}</p>
</Link>
<p>{item.location}</p>
</div>
<div className="flex flex-wrap justify-between">
<p className="text-gray-500">{item.company} - <b className="text-primary-green">{item.type}</b></p>
<p>{item.created_at}</p>
</div>
</div>
</div>
))}
</div>
</>
)
}
export default Home;
Although you have two fields, you only use one event handler for them and both of these fields update the same state. Instead of using one state (find) for the two fields, you could use two states (location and description) and update them on input change. In findJobsAPI you don't need to provide any arguments as the function has access to these values.
function App() {
const [jobs, setJobs] = React.useState([]);
const [description, setDescription] = React.useState('');
const [location, setLocation] = React.useState('');
const [loading, setLoading] = React.useState(false);
// ...
const findJobsAPI = () => {
setLoading(true);
const URL = encodeURIComponent(
`https://jobs.github.com/positions.json?description=${description}&location=${location}`
);
fetch(`https://api.allorigins.win/raw?url=${URL}`)
.then((res) => res.json())
.then((data) => {
setJobs(data);
setLoading(false);
});
// you should handle errors!
};
const handleSubmit = (e) => {
e.preventDefault();
findJobsAPI();
setDescription('');
setLocation('');
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>
Job Description
<input
placeholder="Filter by title, benefits, companies, expertise"
name="desc"
onChange={(e) => setDescription(e.target.value)}
value={description}
/>
</label>
</div>
<div>
<label>
Location
<input
placeholder="Filter by city, state, zip code or country"
name="location"
onChange={(e) => setLocation(e.target.value)}
value={location}
/>
</label>
</div>
<button type="submit">Search</button>
<pre>{JSON.stringify({ loading, description, location }, null, 2)}</pre>
<pre>{JSON.stringify({ jobs }, null, 2)}</pre>
{/* ... */}
</form>
// ...
);
}
ReactDOM.render(<App />, document.getElementById('root'));
body {
font-family: sans-serif;
}
button,
input {
font-family: inherit;
font-size: inherit;
padding: 0.325rem 0.5rem;
}
input {
display: block;
margin-bottom: 0.5rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>