Unable to loop single react component - reactjs

I'm trying to loop my StudentInfo component so a blurb of information can be repeated down an array of 25 objects provided through an API for each object/a student (name, email, company, etc.) I'm not
sure where I'm going wrong; here is my attempted loop via the map function:
export default function StudentList() {
let students = useState(null);
return (
<div className="StudentList">
<div className="row">
<div className="col">
{students.map(function (student, index) {
if (index <= 25) {
return (
<div className="col" key={index}>
<StudentInfo data={student} />
</div>
);
}
})}
</div>
</div>
</div>
);
}
Can someone see what I'm missing or what I might have skipped? I usually assume that I must be doing something wrong because I'm still new at React and trying to adapt other code I used for a weather forecast app to be used for this, but I don't think it's translating over well.
When I run this, I see the first object twice, i.e. Name, Email, Company, etc. it shows the info for the same person twice, rather than the first person, the second person, etc. I want to be able to see this pull all the objects from the array.
Here is the information I'm pulling to return as an entry on student info:
export default function StudentInfo() {
const [info, addInfo] = useState(" ");
function setInfo(response) {
addInfo({
number: response.data.students[0].id,
first: response.data.students[0].firstName,
last: response.data.students[0].lastName,
email: response.data.students[0].email,
company: response.data.students[0].company,
skill: response.data.students[0].skill,
average: response.data.students[0].grades[0],
});
}
let url = "https://api.hatchways.io/assessment/students";
axios.get(url).then(setInfo);
return (
<div className="StudentInfo">
<h1>{info.number}.</h1>
<h2>
Name: {info.first} {info.last}
</h2>
<h2>Email: {info.email}</h2>
<h2>Company: {info.company}</h2>
<h2>Skill: {info.skill}</h2>
<h2>Average: {info.average}</h2>
</div>
);
}
I'm using "if (index <= 25)" as there are 25 total entries that I want showing, but as I mentioned, I have no doubt I'm going about this incorrectly. I want this to loop through all 25 objects with the above information, as I keep saying. I'm sorry if I'm not speaking technically enough to be understood, as I am still learning.
I just want this to return 25 times with info from each object so that it's all listed out.
This is what it currently looks like
UPDATE
I've been tinkering and was able to repeat the entry, however I'm now having trouble getting the unique information, i.e. I'm only seeing the first entry over and over. I'm not sure how to reiterate with new information since it's only going to response.data.students[0]. This is what my code looks like now, where the mapping is:
export default function StudentList() {
let [loaded, setLoaded] = useState(false);
let [students, setStudent] = useState(" ");
function doInfo(response) {
console.log(response.data);
setStudent(response.data.students[0].id);
setLoaded(true);
}
if (loaded) {
return (
<div className="StudentList">
<div className="row">
<div className="col">
{students.map(function (id) {
return <StudentInfo data={id} />;
})}
</div>
</div>
</div>
);
} else {
let url = "https://api.hatchways.io/assessment/students";
axios.get(url).then(doInfo);
}
}
Can someone help me code it so it runs through all 25 entries, not just the first one?

There are some errors in your code.
UseState
React useState hook returns an array with a value and a setter method to update the state useState docs.
const [students, setStudents] = useState(null);
Iterate over null values
If your state starts with a null value you will not be able to iterate over it. To avoid getting an error you should make sure to use the map operator when your state has a value.
{students && students.map(function (student, index) {
...
})}
Handling side effects
You should move your API request (and set your info state) inside of a useEffect (useEffect docs). This way you will set those values asynchronously after the component is mounted.
export default function StudentInfo() {
const [info, addInfo] = useState(null);
useEffect(() => {
async function fetchInfo() {
const url = "https://api.hatchways.io/assessment/students"; //should be a constant outside the component
const response = await axios.get(url);
addInfo({
number: response.data.students[0].id,
first: response.data.students[0].firstName,
last: response.data.students[0].lastName,
email: response.data.students[0].email,
company: response.data.students[0].company,
skill: response.data.students[0].skill,
average: response.data.students[0].grades[0],
});
fetchInfo();
}, [])
return (
<div className="StudentInfo">
{info &&
<h1>{info.number}.</h1>
<h2>
Name: {info.first} {info.last}
</h2>
<h2>Email: {info.email}</h2>
<h2>Company: {info.company}</h2>
<h2>Skill: {info.skill}</h2>
<h2>Average: {info.average}</h2>
}
</div>
);
}

export default function StudentList() {
const [students, setStudents] = useState([]);
Check React's documentation React.
Also check if it cannot be declared that way, you need to use a ternary if. Check that part in React's documenation Conditional rending
{students.map(function (student, index) {
index <= 25 && (
<div className="col" key={index}>
<StudentInfo data={student} />
</div>
);

Related

Remove text by clicking on it - React

I'm trying to start learning react but fail understanding basic logic.
I have a todo list page, which works fine with a strike-through, but if I try to change the strike through to REMOVE instead, my app disappears on click.
Here's my code, hopefully you can understand:
function Note({ notes, note, onClickSetter }) {
const { input, id } = note
const [strikeThrough, setStrikeThrough] = useState(false);
function onNoteClick(event) {
const { value, id } = event.target
//setStrikeThrough((prev) => !prev) - the strike through which is canceled right now
onClickSetter(prev => prev.filter(aNote => aNote.id !== id)) // why this doesn't work?
}
return (
<>
<h1 style={ strikeThrough ? {textDecoration: 'line-through'} : { textDecoration: 'none' }} id={id} onClick={onNoteClick}>{input}</h1>
</>
)
}
a little explanation on my props:
notes - literally the list of notes which comes from a useState on father component (we shouldn't touch this from my understanding of react)
note - self note information
onClickSetter - the other part of useState, the setter one.
So on another words, I have the notes which holds all notes, and onClickSetter which is in another words is setNotes - both part of useState
on top of that I have a note information, because this is a note component
the father component:
function Body() {
const [Notes, setNotes] = useState([])
return (
<div className='notes-body'>
<NewNote onClickSetter={setNotes}/>
{Notes.map((note) => { return <Note key={note.id} notes={Notes} note={note} onClickSetter={setNotes}/>})}
</div>
)
}
function NewNote({ onClickSetter }) {
const [input, setInput] = useState('')
function onInputChange(event) {
const { value } = event.target
setInput(value)
}
function onButtonClick(event) {
onClickSetter((prev) => {
try {
return [...prev, {input: input, id: prev[prev.length-1].id+1}]
}catch{
return [{input: input, id: 0}]
}
})
setInput('')
}
return (
<>
<Input placeholder="add new note" className='note-text' onChange={onInputChange} value={input}/>
<Button className='btn btn-primary add-note' onClick={onButtonClick} />
</>
)
}
The reason is that event.target.id is a string representing a number since all HTML attributes has the string type. Whilst in your data structure, the ID is a number. So, e.g. "1" vs 1. This can be hard to spot sometimes.
The easiest way to fix this is to add a parseInt to the right place to convert the string to a number:
onClickSetter((prev) => prev.filter((aNote) => aNote.id !== parseInt(id)))
However, I also want to mention (and this is more advanced stuff but I like to get people on the right track :) ) that really, you shouldn't pass the whole setter down into the child component, but instead a callback called something like onRemoveNote that accept the note id and the actual filtering/removal would happen in the parent component.
This would be better placement of concerns. For now though, the above will work and I can help you out on stack overflow chat if needed :).

React, Cannot read properties of undefined Error with useEffect

I'm trying to get some weather data from an API, but I always get the same error of not being able to read properties of undefined. I've gone through different tutorials and previously asked issues, but I haven't been able to figure out what I'm doing wrong. Could anyone please give me a hand?
export default function Weather(){
const apiKey = process.env.REACT_APP_API_KEY
const weatherUrl = `http://api.weatherapi.com/v1/current.json?key=${apiKey}&q=Saxthorpe&aqi=no`
const [weatherData, setWeatherData] = useState();
const [error, setError] = useState(null);
useEffect(() => {
(
async function(){
try {
const response = await axios.get(weatherUrl);
setWeatherData(response.weatherData);
} catch (error) {
setError(error);
}
}
)();
}, [])
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData.location.name}</p>
<p className="temp">{weatherData.current.temp_c}</p>
<p className="weather-desc">{weatherData.current.condition.text}</p>
</div>
)
}
When pulling data like this and rendering components conditional on that data, you should account for situations in which the data is not yet available or null.
Specifically, you're attempting to render this data:
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData.location.name}</p>
<p className="temp">{weatherData.current.temp_c}</p>
<p className="weather-desc">{weatherData.current.condition.text}</p>
</div>
But it's not going to available on the first render (i.e. weatherData does not have a location property at first, since your default useState value is undefined).
There are many ways around this, and what you choose ultimately depends on your project and preferences.
You can use optional chaining as a simple protection against null references when checking nested properties:
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData.location?.name}</p>
<p className="temp">{weatherData.current?.temp_c}</p>
<p className="weather-desc">{weatherData.current?.condition?.text}</p>
</div>
Or you can return something else if weatherData is not ready. A good tool for this kind of thing is swr:
import useSWR from 'swr'
function Weather()
{
const { weatherData, error } = useSWR(weatherUrl, fetcher)
if (error) return <div>failed to load</div>
if (!weatherData) return <div>loading...</div>
return <div>hello {weatherData.location}!</div>
}
As a side note, another thing to consider is your useEffect dependencies:
useEffect(() => {
(
async function(){
try {
const response = await axios.get(weatherUrl);
setWeatherData(response.weatherData);
} catch (error) {
setError(error);
}
}
)();
}, [])
With an empty dependency array, your effect runs only on mount and unmount. If you want it to run based on some other variable(s) changing, add those variables to the dependency array.
You can debug to check the response. I think the respose is undefined from
const response = await axios.get(weatherUrl);
response = undefined => can not get weatherData property.
We are using useEffect you can debug on it by F12 in Chrome and see what happen and the reason of this bug. This is better than you come here to ask
Look: weatherData is your state, which is initially... nothing, because you don't pass any data.
So, you cannot access the location field on the first render because it does not exist yet.
It would help if you made sure weatherData exist:
return (
<div className="weather-feature">
<h1>hi</h1>
<p className="location">{weatherData?.location.name}</p>
<p className="temp">{weatherData?.current.temp_c}</p>
<p className="weather-desc">{weatherData?.current.condition.text}</p>
</div>
)

React + Api+ Context

I have a simple React app. On the 'home' page you can search movies from an API and add a movie to a list of favorited. I'm using Context to store which movies are on the list and pass it to the 'favorites' page where those items are rendered. It works well up to a point.
Once on the 'favorites' page, when I remove a movie, I would like the page to then show the updated elements. Instead, I have the elements I already had there plus the elements from the updated list.
So let's say my favorited movies were 'spiderman', 'batman' and 'dracula'. when I remove 'dracula' from the list, I suddenly have the cards of 'spiderman', 'batman, 'dracula', 'spiderman'(again) and 'batman'(again).
When I reload the 'favorites' page, it all works as intended. I just would like for it to be updated correctly upon removing the movie.
Any advice?
Here is the code for the Home page, Favorite page, DataContext and the Card component
import React, { createContext, useState, useEffect } from "react";
export const DataContext = createContext();
function DataContextProvider({ children }) {
const [favorited, setFavorited] = useState([]);
useEffect(() => {
const savedMovies = localStorage.getItem("movies");
if (savedMovies) {
setFavorited(JSON.parse(savedMovies));
}
}, []);
useEffect(() => {
localStorage.setItem("movies", JSON.stringify(favorited));
}, [favorited]);
function addToFavorites(id) {
setFavorited((prev) => [...prev, id]);
}
function removeFromFavorited(id) {
const filtered = favorited.filter(el => el != id)
setFavorited(filtered)
}
return (
<DataContext.Provider value={{ favorited, addToFavorites, removeFromFavorited}}>
{children}
</DataContext.Provider>
);
}
export default DataContextProvider;
function Favorites(props) {
const ctx = useContext(DataContext);
const [favoriteMovies, setFavoriteMovies] = useState([]);
useEffect(() => {
const key = process.env.REACT_APP_API_KEY;
const savedMovies = ctx.favorited;
for (let i = 0; i < savedMovies.length; i++) {
axios
.get(
`https://api.themoviedb.org/3/movie/${savedMovies[i]}?api_key=${key}&language=en-US`
)
.then((res) => {
setFavoriteMovies((prev) => [...prev, res.data]);
});
}
}, [ctx.favorited]);
return (
<>
<Navbar />
<main>
<div className="favorites-container">
{favoriteMovies.map((movie) => {
return <Card key={movie.id} movie={movie} />;
})}
</div>
</main>
</>
);
}
function Home(props) {
const [moviesData, setMoviesData] = useState([]);
const [numOfMovies, setNumOfMovies] = useState(10);
const [search, setSearch] = useState(getDayOfWeek());
const [spinner, setSpinner] = useState(true);
const [goodToBad, setGoodToBad] = useState(null);
function getDayOfWeek() {
const date = new Date().getDay();
let day = "";
switch (date) {
case 0:
day = "Sunday";
break;
case 1:
day = "Monday";
break;
case 2:
day = "Tuesday";
break;
case 3:
day = "Wednesday";
break;
case 4:
day = "Thursday";
break;
case 5:
day = "Friday";
break;
case 6:
day = "Saturday";
break;
}
return day;
}
function bestToWorst() {
setGoodToBad(true);
}
function worstToBest() {
setGoodToBad(false);
}
useEffect(() => {
const key = process.env.REACT_APP_API_KEY;
axios
.get(
`https://api.themoviedb.org/3/search/movie?api_key=${key}&query=${search}`
)
.then((res) => {
setMoviesData(res.data.results);
//console.log(res.data.results)
setSpinner(false);
setGoodToBad(null);
});
}, [search]);
return (
<>
<Navbar />
<main>
<form>
<input
type="text"
placeholder="Search here"
id="search-input"
onChange={(e) => {
setSearch(e.target.value);
setNumOfMovies(10);
}}
/>
{/* <input type="submit" value="Search" /> */}
</form>
<div className="sorting-btns">
<button id="top" onClick={bestToWorst}>
<BsArrowUp />
</button>
<button id="bottom" onClick={worstToBest}>
<BsArrowDown />
</button>
</div>
{spinner ? <Loader /> : ""}
<div>
<div className="results">
{!moviesData.length && <p>No results found</p>}
{moviesData
.slice(0, numOfMovies)
.sort((a,b) => {
if(goodToBad) {
return b.vote_average - a.vote_average
} else if (goodToBad === false){
return a.vote_average - b.vote_average
}
})
.map((movie) => (
<Card key={movie.id} movie={movie} />
))}
</div>
</div>
{numOfMovies < moviesData.length && (
<button className="more-btn" onClick={() => setNumOfMovies((prevNum) => prevNum + 6)}>
Show More
</button>
)}
</main>
</>
);
}
export default Home;
function Card(props) {
const ctx = useContext(DataContext);
return (
<div
className={
ctx.favorited.includes(props.movie.id)
? "favorited movie-card"
: "movie-card"
}
>
<div className="movie-img">
<img
alt="movie poster"
src={
props.movie.poster_path
? `https://image.tmdb.org/t/p/w200/${props.movie.poster_path}`
: "./generic-title.png"
}
/>
</div>
<h2>{props.movie.original_title}</h2>
<p>{props.movie.vote_average}/10</p>
<button
className="add-btn"
onClick={() => ctx.addToFavorites(props.movie.id)}
>
Add
</button>
<button
className="remove-btn"
onClick={() => ctx.removeFromFavorited(props.movie.id)}
>
Remove
</button>
</div>
);
}
export default Card;
As mentioned before a lot of things cold be improved (you might want to check some react tutorial beginners related to best practices).
Anyway the main issue your app seems to be your callback after you get the response from the API (so this part):
useEffect(() => {
const key = process.env.REACT_APP_API_KEY;
const savedMovies = ctx.favorited;
for (let i = 0; i < savedMovies.length; i++) {
axios
.get(
`https://api.themoviedb.org/3/movie/${savedMovies[i]}?api_key=${key}&language=en-US`
)
.then((res) => {
setFavoriteMovies((prev) => [...prev, res.data]);
});
}
here you are calling setFavoriteMovies((prev) => [...prev, res.data]); but you actually never reset your favoriteMovies list.
So in your example favoriteMovies is ['spiderman', 'batman', 'dracula']. Then the useEffect callback executes with the array unchanged.
So you are making the requests just for 'spiderman' and 'batman' but your favoriteMovies array is ['spiderman', 'batman', 'dracula'] when the callback is entered (and this is why you end up appending those values to the existing ones and in the end your favoriteMovies == ['spiderman', 'batman', 'dracula', 'spiderman', 'batman'] in your example).
How to fix?
Quick fix would that might be obvious would be to reset the favoriteMovies at the beggining of useEffect. But that would be a extremly bad ideea since setting the state many times is terrible for performance reasons (each setState callback triggers a re-render) as well as for redability. So please don't consider this.
What I would suggest though would be to get all the values in the useEffect callback, put all the new favorite movies data in a variable and at the end of the function change the state in one call with the full updated list.
There are multiple ways to achieve this (async await is the best imo), but trying to alter the code as little as possible something like this should also work:
useEffect(() => {
const key = process.env.REACT_APP_API_KEY;
const savedMovies = ctx.favorited;
const favoriteMoviesPromises = [];
for (let i = 0; i < savedMovies.length; i++) {
favoriteMoviesPromises.push(
axios
.get(`https://api.themoviedb.org/3/movie/${savedMovies[i]}?api_key=${key}&language=en-US`)
.then((res) => res.data)
);
}
Promise.all(favoriteMoviesPromises).then((newFavoriteMovies) =>
setFavoriteMovies(newFavoriteMovies)
);
});
Please note I wasn't able to test this code since I don't have an exact reproduction of the error (so it might need some small adjustments). This code sample is rather a direction for your problem :)
Edit regarding the comment:
Despite the state issue, I would really recommend working on code cleanliness, efficiency and readability.
Examples (I put a few examples in code snippets to avoid a really long comment):
1. `function getDayOfWeek`:
First thing is that you don't need the `day` variable and all the break statement.
You could just return the value on the spot (this would also stop the execution of the function).
So instead of
case 0:
day = "Sunday";
break;
you could have
case 0:
return "Sunday";
Going even further you don't need a switch case at all. You could just create an array
`const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', "Saturday"]`
and just return daysOfWeek[date].
This would result in shorter and easier to read code.
2. Also this code is not really consistent. For example you have
onChange={(e) => {
setSearch(e.target.value);
setNumOfMovies(10);
}}
but also `onClick={bestToWorst}` which is just `function bestToWorst() { setGoodToBad(true) }`.
If this is not reusable you could just use `onClick={() => setGoodToBad(true)}`.
But even if you really want to keep the bestToWorst callback for whatever reason you could at least write and inline function
(something like `const bestToWorst = () => setGoodToBad(true)` and use it the same).
Anyway... From thoose 2 cases (bestToWorst and `Search here` onChange function),
the second one make more sense to be defined outside.
3. The next part is really hard to read and maintain:
{!moviesData.length && <p>No results found</p>}
{moviesData
.slice(0, numOfMovies)
.sort((a,b) => {
if(goodToBad) {
return b.vote_average - a.vote_average
} else if (goodToBad === false){
return a.vote_average - b.vote_average
}
})
.map((movie) => (
<Card key={movie.id} movie={movie} />
))}
Also this code doesn't belong in html.
You should at least put the slice and sort parts in a function.
Going further `if(goodToBad)` and `else if (goodToBad === false)` are also not ideal.
It would be best to use a separate function an example would be something like:
const getFormattedMoviesData = () => {
let formattedMoviesData = moviesData.slice(0, numOfMovies)
if(!goodToBad && goodToBad !== false) return formattedMoviesData;
const getMoviesDifference = (m1, m2) => m1.vote_average - m2.vote_average
return formattedMoviesData.sort((a,b) => goodToBad ? getMoviesDIfference(b,a) : getMoviesDIfference(a,b)
4. DataContext name doesn't suggest anything.
I would propose something more meaningfull (especially for contexts) like `FavoriteMoviesContext`.
In this way people can get an ideea of what it represents when they come across it in the code.
Additionally the context only contains `favorited, addToFavorites, removeFromFavorited`.
So rather than using
`const ctx = useContext(DataContext);`
you could just use
`const {favorited, addToFavorites, removeFromFavorited} = useContext(DataContext);`
and get rid of the ctx variable in your code
Regarding the api:
If the search api returns all the movie data you need you can take it from there and use it in the favorites.
Alternatively it would be great to have an endpoint to return a list of multiple movies
(so send an array of id's in the request and receive all of them).
But this is only possible if the backend supports it.
But otherwise, since the api might contain hundreds of thousands or even millions, having them all stored on the frontside state would be an overkill
(you can in some cases have this type lists stored in a redux state or a react context and filter them on frontend side.
But it won't be efficient for such a big volume of data).
Small conclusion: ignoring the state part there aren't big issues in the code (and for a personal project or for learning might be decent). But if someone else has to work on in or you have to come back on this code after a month might become a nightmare. (especially since it seems like the codebase is not very small)
And people trying to understand your code might find it hard as well (including when you are posting it on stack overflow). I highlighted just a few, but it should point in the right direction, I hope.
First of all, you should review the way you manage the favorite movies and that of what you want to do with them in your app. If you need to make a page to display the list of favorites, I would rather save in localstorage the necessary information for the list (cover, title, year, id, etc) without having to save the whole movie object. This will prevent you from having to call the API for each movie which will be very bad in terms of performance on your application. Also, it will prevent you from having to create another state on the Favorites page so it will solve your problem automatically (I think your problem came from the duplicate state you have).

Having trouble with react error: 'Objects are not valid as a React child. If you meant to render a collection of children, use an array instead'

I am creating a React app with a Django backend and using Redux to maintain state. I am trying to render data in a child component and I keep getting this error: Objects are not valid as a React child (found: object with keys {id, sets, reps, weight, bridge_id}). If you meant to render a collection of children, use an array instead.
What I have is a main screen RoutineScreen.js which is supposed to render child components displaying the exercises in the routine and sets, reps, weight for each.
This is what my code looks like:
function RoutineScreen() {
// Get routine id which is passed in through program screen components
const { id } = useParams()
const routine_id = id
//Extract program id from routine
const state = {...store.getState()}
let program_id
// -- set routines, which we will loop through to find program id
const routines = state.programRoutines.routines
// -- loop through routines, find match for routine_id
for (let i in routines) {
if (i.id == routine_id){
program_id = i.program
}
}
const dispatch = useDispatch()
const routineExercises = useSelector(state => state.routineExercises)
const { error, loading, exercises } = routineExercises
useEffect(() => {
dispatch(listRoutineExercises(program_id, routine_id))
}, [dispatch])
return (
<div className="screen-container">
<Header/>
<SearchBar/>
<div className="card-container">
{exercises.map((exercise, index) => (
// Render Routines
<Exercise key={exercise.id} routine_id={routine_id} index={index} exercise={exercise}/>
))}
</div>
</div>
)
}
export default RoutineScreen
function Exercise({ exercise, routine_id, index }) {
// Get routine id which was passed in through program screen components
const { id } = useParams()
const exercise_id = id
const dispatch = useDispatch()
// use spread operator to unpack elements from exerciseParameters
const exerciseParameters = useSelector(state => state.exerciseParameters)
const { error, loading, parameters } = exerciseParameters
useEffect(() => {
dispatch(listExerciseParams(routine_id, exercise_id))
}, [dispatch])
return (
<div style={{ textDecoration: 'none' }}>
<div>
<h3>{exercise.name} </h3>
<h4>{parameters[index].reps}</h4>
<h4>{parameters[index].weight}</h4>
</div>
</div>
)
}
export default Exercise
Here is my data:
Note: I am completely new to React and Redux so feel free to let me know if there are also any other suggestions for how I should fix my code or if I am missing any relevant information for this problem. Thanks!
I tried to access the specific elements in the object by using parameters[index].reps, expecting that it would then display that data, but instead I received the error above.
I think you can do something like :
return(
<div style={{ textDecoration: 'none' }}>
<div>
<h3>{exercise.name} </h3>
{parameters.map(parameter => (
<>
<h4>{parameter.reps}</h4>
<h4>{parameter.weight}</h4>
</>
))}
</div>
</div>
)

Single responsibility in React component

I was learning Single responsibility principle in React and created such component:
import React from "react";
import {useGetRemoteData} from "./useGetRemoteData";
export const SingleResponsibilityPrinciple = () => {
const {filteredUsers , isLoading} = useGetRemoteData()
const showDetails = (userId) => {
const user = filteredUsers.find(user => user.id===userId);
alert(user.contact)
}
return <>
<div> Users List</div>
<div> Loading state: {isLoading? 'Loading': 'Success'}</div>
{filteredUsers.map(user => {
return <div key={user.id} onClick={() => showDetails(user.id)}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
</>
}
As you can see above, I have this code
const user = filteredUsers.find(user => user.id===userId);
The question is Is it best practice that if whenever we use map, reduce or any array function in React component, we should extract that logic out of a component, that is, filteredUsers.find(user => user.id===userId); should be extracted and we need to create utility function. So, function should not care about how a particular thing is done. Is it true?
Thank you for your question. I want to advice as follows
It is better for your to check if filteredUsers exists or not in your return.
{filteredUsers?.map(user=>{
//your code
})
You don't need to get specific user as you already loop in your map method. Just simply do something like this
{filteredUsers.map(user => {
return <div key={user.id} onClick={() => showDetails(alert(user.contact))}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
Remember the difference between find, filter method of Javascript array. If you have unique value according to userId, you simply use find method to get the first value not array, if you use filter, you get arrays of the condition. Are you sure you don't need to alert(user[0].contact), not alert(user.contact)?

Resources