How to fetch unique item from redux with conditional operator? - reactjs

I am trying to fetch only unique data from the redux store but got all values from the redux store.
I use usePerma hook to check id from URL and id of redux data then apply ternary operator but did not get desired output. See the output image and code and please tell how to get only one value from that.
When URL is 1002 only 1002 is item should show but its also showing 1001 item.
Here is my code:
import React from 'react'
import { NavLink } from 'react-router-dom';
import { useStateValue } from './Stateprovider';
import { useParams } from 'react-router';
const User = () => {
const [{basket}, dispatch] = useStateValue();
const {id} = useParams();
return (
<>
{basket.map((item) => (
<>
{
({id} === {item.id}) ? //Error
<div key="id" className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home' > User Info </NavLink>
</div>
: null
}
</>
))}
</>
)
}
export default User;

If I understand your question correctly, you want to map only the items from the basket array that match the id match param. For this you can either use Array.prototype.find to first find the matching item and conditionally render if found, or use Array.prototype.filter and just filter the array inline.
Filtering the array inline:
const User = () => {
const [{basket}, dispatch] = useStateValue();
const { id } = useParams();
return (
<>
{basket
.filter(item => item.id === id)
.map((item) => (
<div key={item.id} className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home'>
User Info
</NavLink>
</div>
))
}
</>
)
}
Finding first, then rendering: remember that .find returns undefined if no match is found
const User = () => {
const [{basket}, dispatch] = useStateValue();
const { id } = useParams();
const item = basket.find(item => item.id === id);
return item ? (
<div className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home'>
User Info
</NavLink>
</div>
) : null;
}

Related

I can't get array.filter to work with useState

I am trying to delete an item from a shopping cart and I am using the filter hook to accomplish this. I have looked at the documentation for this and at the answers here on stack overflow. unfortunately no luck.
this is my code for the entire component. the function is of course "deleteItemFromBasket" and it is being called at the onclick on the delete button:
function CheckoutProduct({id, title, price, description, rating, category, image }) {
const [basket, addToBasket] = useAppContext();
const deleteItemFromBasket = (id) => {
addToBasket(basket.filter((task) => task.id !== id));
};
return (
<div>
{basket.map((element) => {
if (element === id) {
return (
<div className='grid grid-cols-5 border-b pb-4'>
{/* far left */}
<Image src={image} height={200} width={200} objectFit='contain' />
{/* middle */}
<div className="col-span-3 mx-5">
<p>{title}</p>
<p className='text-xs my-2 line-clamp-3'>{description}</p>
<button onClick={deleteItemFromBasket} className='button'>delete</button>
<h1>items ID in basket: {basket}</h1>
<h1>length of array: {basket.length}</h1>
</div>
{/* right */}
<div>
<p>${price}</p>
</div>
</div>
)
}
})}
</div>
)
}
here is the code of the context provider
import React, { createContext, useContext, useState } from 'react';
const AppContext = createContext();
export function AppWrapper({ children }) {
var [basket, addToBasket]= useState([]);
return (
<AppContext.Provider value={[basket, addToBasket]}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
looks like basket is an array of ids, not an array of objects. Assuming that is the case, then you need to change your delete function to
const deleteItemFromBasket = (id) => {
addToBasket(basket.filter((element) => element !== id));
};
This is assuming that addToBasket is actually just setting the basket, not just additive.
I eventually got it to work and it took a combination of the two answers so thank you everyone for the help!
notice below that the function "deleteItemFromBasket" is the same as joe's post but it to get it to work I needed to add the function in the onClick like zehan's answer. I don't know why this works so if anyone has an explanation as to why I'll save the answer spot for it! thanks friends
import React from 'react';
import Image from 'next/image'
import { useAppContext } from '../state'
function CheckoutProduct({id, title, price, description, rating, category, image }) {
const [basket, addToBasket] = useAppContext();
const deleteItemFromBasket = (item) => {
addToBasket((current) => current.filter((element) => element !== item));
};
return (
<div>
{basket.map((element) => {
if (element === id) {
return (
<div className='grid grid-cols-5 border-b pb-4'>
{/* far left */}
<Image src={image} height={200} width={200} objectFit='contain' />
{/* middle */}
<div className="col-span-3 mx-5">
<p>{title}</p>
<p className='text-xs my-2 line-clamp-3'>{description}</p>
{/* <button onClick={deleteItemFromBasket(element)} >delete item</button> */}
<button onClick={ ()=> deleteItemFromBasket(id)} className='button'>delete</button>
<h1>items ID in basket: {basket}</h1>
<h1>length of array: {basket.length}</h1>
</div>
{/* right */}
<div>
<p>${price}</p>
</div>
</div>
)
}
})}
</div>
)
}

How to display the list of followers on the profile of all users

I try to display the list of followers and followings on the profile of the users using their id, when I implement there is only the list of followers and followings of the current user that is connected, even if I follow the profile of another user
My Component
import React, { useState } from "react";
import { useSelector } from "react-redux";
import { Link, useParams } from "react-router-dom";
import Modal from "../Components/Log/Modals";
import { isEmpty } from "../Components/Utils";
import FollowHandler from "./FollowHandler";
const FollowingPopup = ({ followerId, type }) => {
const userData = useSelector((state) => state.userReducer);
const [Opening, setOpening] = useState(false);
const { id: userId } = useParams();
const [followingsPopup, setFollowingsPopup] = useState(false);
const usersData = useSelector((state) => state.usersReducer);
return (
<Modal open={!Opening} onClick={() => setOpening(Opening)}>
<span
onClick={() => setOpening(!Opening)}
className="followers__modal__close"
></span>
<header className="followers__modal__header">
<button
className="followers active-followers"
>
Mes Abonnements
</button>
</header>
{/**Les personnes suivis par l'utilsateur */}
{!followingsPopup && (
<>
<div className="followers__modal__content">
<ul>
{userId ? (usersData.map((user) => {
let follower = user.follower.map((users) => users.followerId)
for (let i = 0; i < usersData.length; i++) {
if (user.id !== userId && follower.includes(user.id)) {
return (
<li key={user.id} className="followers__modal__followers">
<a
href={`/${user.id}`}
className="followers__modal__followers__links"
>
<img src={user.picture} alt="" />
<h4>{user.firstname + " " + user.lastname} </h4>
</a>
{user.id !== userData.id ? < FollowHandler followerId={user.id} type={'suggestion'} /> : null}
</li>
);
}
}
return null;
})) : null}
</ul>
</div>
</>
)}{" "}
{/**Les personnes abonnés à l'utilsateur */}
</Modal>
);
};
export default FollowingPopup;
this code works perfectly and I manage to display the list of followers of current user who is logged in but when I click on the profile of another user, the same list of followers of the user who is logged in is displayed too, not the selected profile
[
Your for loop is unnecessary there.
This might be working for you but without a sandbox to test it's hard to tell.
const userFollowers =usersData.find(user=>user.id===userId).followers.map(follower=>follower.id);
usersData.map(user=>{
if(userFollowers.includes(user.id)){
return (
<li key={user.id} className="followers__modal__followers">
<a
href={`/${user.id}`}
className="followers__modal__followers__links"
>
<img src={user.picture} alt="" />
<h4>{user.firstname + " " + user.lastname} </h4>
</a>
{user.id !== userData.id ? < FollowHandler followerId={user.id} type={'suggestion'} /> : null}
</li>
)
})

Why clicking Back to the list sends me pack to front page? Using hooks

I'm setting a value when you click on Back to List. As per logic it should not go back to the 1st page which appears when I load the browser. I specified a value
<a href="#" onClick={() => setPokemon(pokemon.name)}>
If I wanted to go back to the list page I should have passed null like this.
<a href="#" onClick={() => setPokemon(null)}>
Here is the full code.
import "./styles.css";
import { QueryClient, QueryClientProvider, useQuery } from "react-query";
import axios from "axios";
import { useState } from "react";
const queryClient = new QueryClient();
function App() {
// useEffect(() => {
// queryClient.clear();
// }, []);
const [pokemon, setPokemon] = useState(null);
return (
<QueryClientProvider client={queryClient}>
<div className="App">
{pokemon ? (
<Pokemon pokemon={pokemon} setPokemon={setPokemon} />
) : (
<PokemonList setPokemon={setPokemon} />
)}
</div>
</QueryClientProvider>
);
}
export default App;
function usePokemonList() {
return useQuery("pokemon", async () => {
const { data } = await axios.get(
"https://pokeapi.co/api/v2/pokemon?offset=0&limit=50"
);
return data.results;
});
}
function usePokemon(name) {
return useQuery(["pokemon", name], async () => {
const { data } = await axios.get(
`https://pokeapi.co/api/v2/pokemon/${name}`
);
return data;
});
}
function Pokemon({ pokemon, setPokemon }) {
const { isLoading, data } = usePokemon(pokemon);
return (
<div>
<a href="#" onClick={() => setPokemon(pokemon.name)}>
Back to the list
</a>
{isLoading ? (
<p>loading...</p>
) : (
<div>
<h1>{pokemon}</h1>
<img src={data.sprites.front_default} alt={pokemon} />
</div>
)}
</div>
);
}
function PokemonList({ setPokemon }) {
const { isLoading, data } = usePokemonList();
return (
<div>
{isLoading ? (
<p>loading...</p>
) : (
<ul>
{data.map((pokemon) => (
<li key={pokemon.name}>
<a onClick={() => setPokemon(pokemon.name)} href="#">
{pokemon.name}
</a>
</li>
))}
</ul>
)}
</div>
);
}
The pokemon variable only holds a string value and is not an object with a name property on it. So in your Pokemon component when you pass pokemon.name in setPokemon function you are essentially passing an undefined value because the property name doesn't exist on the string.
And based on the conditional rendering you are doing in the App component since undefined is a falsy value, it renders PokemonList component instead of retaining the Pokemon component. To retain the Pokemon component, use setPokemon(pokemon) instead of setPokemon(pokemon.name).
function Pokemon({ pokemon, setPokemon }) {
//pokemon variable is of string type.
console.log(pokemon) //Prints the selected pokemon
const { isLoading, data } = usePokemon(pokemon);
return (
<div>
<a href="#" onClick={() => setPokemon(pokemon.name)}> {/* Passing pokemon.name is passing a property value that doesn't exist on the object*/}
Back to the list
</a>
To avoid going back to the list, set the pokemon to its actual name or a non-falsy value.
<a href="#" onClick={() => setPokemon(pokemon)}>
Back to the list
</a>

How can I create Single Page

How can I pass map items (title, category and images) in my id.jsx file.
Basically, I just want to create a single page for my projects. But I can only access post ID. I don't know how to pass other data items.
'Projects folder'
[id].js
import { useRouter } from "next/router";
const Details = () => {
const router = useRouter();
return <div>Post #{router.query.id}
// Single Project Title = {project.title} (like this)
</div>;
};
export default Details;
index.js
import { MyProjects } from "./MyProjects";
const Projects = () => {
const [projects, setProjects] = useState(MyProjects);
{projects.map((project) => (
<Link
href={"/projects/" + project.id}
key={project.id}
passHref={true}
>
<div className="project__item">
<div className="project__image">
<Image src={project.image} alt="project" />
</div>
<div className="project_info">
<h5>{project.category}</h5>
<h3>{project.title}</h3>
</div>
</div>
</Link>
))}
If I understand your question correctly, you want to send some "state" along with the route transition. This can be accomplished using an href object with the "state" on the query property, and the as prop to hide the query string.
Example:
{projects.map((project) => (
<Link
key={project.id}
href={{
pathname: "/projects/" + project.id,
query: {
id: project.id,
category: project.category,
title: project.title
}
}}
passHref={true}
as={"/projects/" + project.id}
>
<div className="project__item">
<div className="project__image">
<Image src={project.image} alt="project" />
</div>
<div className="project_info">
<h5>{project.category}</h5>
<h3>{project.title}</h3>
</div>
</div>
</Link>
))}
...
const Details = () => {
const router = useRouter();
return (
<div>
<div>Post #{router.query.id}</div>
<div>Title {router.query.title}</div>
<div>Category {router.query.category}</div>
</div>
);
};

React - Warning: Each child in a list should have a unique "key" prop

In this simple React App, I don't understand why I get the following warning message:
Warning: Each child in a list should have a unique "key" prop.
To me it seems that I put the key at the right place, in form of key={item.login.uuid}
How can I get rid of the warning message?
Where would be the right place to put the key?
App.js
import UserList from './List'
const App = props => {
const [id, newID] = useState(null)
return (
<>
<UserList id={id} setID={newID} />
</>
)
}
export default App
List.js
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const renderItem = (item, newID) => {
return (
<>
{newID ? (
// User view
<div key={item.login.uuid}>
<div>
<h2>
{item.name.first} {item.name.last}
</h2>
<p>
{item.phone}
<br />
{item.email}
</p>
<button onClick={() => setID(null)}>
Back to the list
</button>
</div>
</div>
) : (
// List view
<li key={item.login.uuid}>
<div>
<h2>
{item.name.first} {item.name.last}
</h2>
<button onClick={() => setID(item.login.uuid)}>
Details
</button>
</div>
</li>
)}
</>
)
}
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <div>{renderItem(user, true)}</div>
} else {
// List view
return (
<ul>
{resources.map(user => renderItem(user, false))}
</ul>
)
}
}
export default UserList
The key needs to be on the root-level element within the loop. In your case, that's the fragment (<>).
To be able to do that, you'll need to write it out fully:
const renderItem = (item, newID) => {
return (
<Fragment key={item.login.uuid}>
{newID ? (
...
)}
</Fragment>
);
}
(You can add Fragment to your other imports from react).
Note that the fragment isn't actually needed in your example, you could drop it and keep the keys where they are since then the <div> and <li> would be the root element:
const renderItem = (item, newId) => {
return newID ? (
<div key={item.login.uuid}>
...
</div>
) : (
<li key={item.login.uuid}>
...
</li>
)
}
What if you create 2 separate components, one for the user view and one for the list item. That way you only need to pass the user prop. Also, use JSX and pass wht key from there.
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const User = ({user}) => (
<div key={user.login.uuid}>
<div>
<h2>
{user.name.first} {user.name.last}
</h2>
<p>
{user.phone}
<br />
{user.email}
</p>
<button onClick={() => setID(null)}>
Back to the list
</button>
</div>
</div>
)
const ListItem = ({user}) => (
<li key={user.login.uuid}>
<div>
<h2>
{user.name.first} {user.name.last}
</h2>
<button onClick={() => setID(user.login.uuid)}>
Details
</button>
</div>
</li>
)
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <User user={user}</div>
} else {
// List view
return (
<ul>
{resources.map((user, index) => <ListItem key={index} user={user} />)}
</ul>
)
}
}
export default UserList

Resources