So i'm creating a job board that will display jobs and, upon clicking the specific job you are interested in, additional information will be shown. The catch is that each time someone click on a specific job post, ALL job posts reveal their additional information. I'm only trying to show the job post that was CLICKED's info. Any help would be appreciated.
App.js
import './App.css';
import React, {useState, useEffect} from 'react'
import jobData from "./assets/data.json"
import JobBoardComponent from './components/JobBoardComponent';
import NavBar from './components/NavBar'
import EmailBar from './components/EmailBar'
import BannerPage from "./components/BannerPage"
import { filter } from 'domutils';
import FuncFilter from "./components/FuncFilter"
import LocFilter from "./components/LocFilter"
import ConFilter from './components/ConFilter';
function App() {
const [jobs, setJobs] = useState([])
const [filtered, setFiltered] = useState([])
const [activeCategory, setActiveCategory] = useState(0)
const [activeLocation, setActiveLocation] = useState(0)
const [activeContract, setActiveContract] = useState(0)
const [toggled, setToggled] = useState(true);
useEffect(() => {
setJobs(jobData)
}, [])
return (
<div className="App w-auto">
<NavBar />
<BannerPage />
<EmailBar />
<FuncFilter
jobs={jobs}
setFiltered={setFiltered}
activeCategory={activeCategory}
setActiveCategory={setActiveCategory}
activeLocation={activeLocation}
setActiveLocation={setActiveLocation}
setActiveContract={setActiveContract}
activeContract={activeContract}
/>
<LocFilter
jobs={jobs}
setFiltered={setFiltered}
activeLocation={activeLocation}
setActiveLocation={setActiveLocation}
setActiveCategory={setActiveCategory}
activeCategory={activeCategory}
setActiveContract={setActiveContract}
activeContract={activeContract}
/>
<ConFilter
jobs={jobs}
setFiltered={setFiltered}
activeLocation={activeLocation}
setActiveLocation={setActiveLocation}
setActiveCategory={setActiveCategory}
activeCategory={activeCategory}
setActiveContract={setActiveContract}
activeContract={activeContract}
/>
<div>
{ activeCategory == 0 ? jobs.map((job) => <JobBoardComponent toggled={toggled} setToggled={setToggled} job={job} key={job.id} />)
: filtered.map((job) => <JobBoardComponent toggled={toggled} setToggled={setToggled}
job={job} key={job.id} />)}
</div>
</div>
);
}
export default App;
JobBoard.js
import React, {useState, useEffect} from "react"
function JobBoardComponent({ toggled, setToggled, job }) {
const langAndTools = []
const skill = job.skills
const tools = job.tools
if (skill && tools) {
langAndTools.push(...skill)
langAndTools.push(...tools)
}
useEffect(() => {
setToggled(toggled)
}, [toggled])
const handleClick = event => {
setToggled(!toggled);
};
return (
<div id="jobboard" onClick={handleClick} className={`flex flex-col hover:bg-gray-100 bg-white shadow-md m-4 mb-20 px-6 -mt-10 rounded ${job.featured && 'border-l-4 border-black'}`}>
<div>
<img className="-mt-10 mb-4 w-20 h-20"src={job.logo} alt={job.company}></img>
</div>
<div className="text-left mt-1">
<h3 className="font-mono font-bold text-blue-600">
{job.company}
{job.fresh && (<span className="text-blue-100 bg-blue-500 font-bold m-2 py-1 px-2 rounded-full">
New</span>)}
{job.featured && (<span className="text-white bg-gray-800 font-bold m-2 py-1 px-2 rounded-full">Featured</span>)}
</h3>
<h2 className="font-mono font-bold text-xl my-2">{job.position}</h2>
<p className="font-mono text-gray-700">
{job.postedAt} · {job.contract} · {job.location}
</p>
</div>
<div id="jobdesc" className={toggled ? "hidden font-mono my-10 " : "font-mono my-10"} >
<h3>Who We Are:</h3>
<p>{job.whoweare}</p>
<br></br>
<h3>Job Description:</h3>
<p>{job.description}</p>
<br></br>
<br></br>
<a id="applybtn" target="_blank" href={job.joblink} className="font-mono text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 font-medium rounded-lg text-sm px-32 py-2 text-center mr-2 content-center"> Apply </a>
<br></br>
</div>
<div className="flex flex-wrap items-center mt-4 mr-4 pt-4 border-t-2 border-gray-300 border-solid">
{langAndTools ? (
langAndTools.map((langAndTool) => (
<span className="text-blue-600 bg-blue-100 font-bold mr-4 mb-4 p-2 rounded">{langAndTool}</span>
))
)
: " "
}
</div>
</div>
)
}
export default JobBoardComponent```
[1]: https://i.stack.imgur.com/UVlzt.png
[2]: https://i.stack.imgur.com/NSoOC.png
CodePudding user response:
In order to only open a specific job post you need to check the id of the clicked job against the id of the job that you're mapping over.
In your handleClick callback you can pass in the selected job id:
onClick={()=> handleClick(job.id)}
In App.js you can set a state to store this value:
const [setSelectedId,setSelectedId] = useState("")
Then in your JobBoardComponent you can set the selected id:
<JobBoardComponent handleClick={(id)=> setSelectedId(id)}
Now you have your selected id stored in state you can check this against the JobBoardComponent you're mapping over:
<JobBoardComponent handleClick={(id)=> setSelectedId(id)} selected={selectedId === job.id}
This will pass a boolean into JobBoardComponent which you can use to then conditionally render whatever you want:
...selected && // show info
This is known as lifting state up and is quite a common pattern in react worth learning. Here are the React docs that explain it in more detail
CodePudding user response:
@msmoorse solution is correct. To add on, the reason why you are facing this problem is because you only have one boolean toggled
to control whether all the jobs are shown. Since all jobs share the same state, they are gonna either all show, or all hide.
If you want only one job to be shown at a time, follow @msmoorse's solution. If you want to allow users to open multiple jobs at the same time, you will need an array or object to record which jobs are shown and which jobs are hidden.
const [jobStates, setJobStates] = useState(Array(numOfJobs).fill(false))
The handleClick function will look something like this
handleClick={(id)=> setJobStates(prevJobStates => {
const newJobStates = prevJobStates
newJobStates[id] = !prevJobStates[id]
return newJobStates
})}