Show data from a card component to a Detail Component using useContext - reactjs

I´m trying to update the Detail component data whenever I select a card component.
I´m using useContext hook but I´m stuck at this point and I don´t know how to do this.
Could anybody please help?.
enter image description here
context.js
import { createContext, useContext, useEffect, useState } from "react";
import api from "./api/players"
const APIcontext = createContext();
export function Provider({children}){
const [players, setPlayers] = useState([]);
const [currentPlayer, setCurrentPlayer] = useState(null)
useEffect(() => {
const fetchPlayers = async () => {
try{
const response = await api.get('/players');
setPlayers(response.data)
}
catch(err){
if(err.response){
console.log(err.response.data)
}else{
console.log(`Error: ${err.message}`)
}
}
}
fetchPlayers()
},[])
const UpdateDetails = () =>{
}
return(
<APIcontext.Provider value={{players, currentPlayer,UpdateDetails}}>{children}</APIcontext.Provider>
)
}
export default APIcontext;
This is the Detail Component where I want to display the data, whenever a card is selected on click.
Details.jsx
import React from "react";
import Button from "react-bootstrap/Button";
import Card from "react-bootstrap/Card";
import {useContext} from "react";
import APIcontext from "../context";
function Details() {
const {players} = useContext(APIcontext)
return (
<Card style={{width:"80%", marginRight:"20px"}}>
<Card.Header className="text-center"><h1>Details</h1>
</Card.Header>
<Card.Body className="px-5">
<h4>{players.realName}</h4>
<h4>{players.realName}</h4>
<h4>{players.assets}</h4>
<Button variant="primary">Go somewhere</Button>
</Card.Body>
</Card>
);
}
export default Details;
This is the Card component that displays the data from the players. By clicking on one of them the Details component should be updated.
Card Component
[enter code here
import React, { useEffect, useState, useContext } from "react";
import Card from "react-bootstrap/Card";
import APIcontext from "../context";
function Cards({}) {
const { players } = useContext(APIcontext);
console.log("players", players);
const { UpdateDetails} = useContext(APIcontext)
return (
<>
{players &&
players.map((player) => (
<Card
key={player.id}
className="mt-4 mx-2"
style={{
display: "flex",
width: "12rem",
height: "9rem",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
<Card.Body onClick={UpdateDetails}>
<Card.Title>{player.realName}</Card.Title>
<Card.Subtitle className="mb-2 text-muted">
{player.playerName}
</Card.Subtitle>
<Card.Text>{player.asset}</Card.Text>
</Card.Body>
</Card>
))}
</>
);
}
export default Cards;][2]
App.js
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Card from "./components/Card";
import Details from "./components/Details";
import { Container, Row, Col } from "react-bootstrap";
import Controls from "./components/Controls";
import { useEffect, useState } from "react";
import { Provider } from "./context";
function App() {
return (
<div className="App">
<Provider>
<Container className="p-3 d-flex flex-row justify-content-between">
<Container className="p-5 d-flex flex-row mb-4 bg-light rounded-3">
<Details />
<Controls />
</Container>
</Container>
<Container className=" mr-5 mb-4 bg-light rounded-3 " style={{width:"65%", float:"left", marginLeft:"10%"}}>
<Container className="d-flex flex-row flex-justify-content-center flex-wrap mr-5 mb-4 bg-light rounded-3" >
<Card className="mr-2"/>
</Container>
</Container>
</Provider>
</div>
);
}
export default App;

Context implementation is easy, you only need to understand its flow and how to update the root values.
You have done everything right, and I believe all that is left is for you to update the Details component on click of the Card component. Here's how you could go about it.
In the Card component, where you've handled the onClick event:
<Card.Body onClick={UpdateDetails}>, change it to: <Card.Body onClick={() => UpdateDetails(player)}>
This will help us send the data of the card we want to show in the Details component.
Inside the context file, in the UpdateDetails method, collect the argument passed while calling the function and set the currentPlayer accordingly like this:
const UpdateDetails = (player) => setCurrentPlayer(player)
Here, the context will get updated and wherever you use the currentPlayer value will receive the new data.
You also need the currentPlayer state inside Details file, import it along with players state:
const {players, currentPlayer} = useContext(ApiContext)
Now you can use currentPlayer.name or any other key from the player object.
Hope this helps. Please accept this answer if it does! Thank you!

Related

React useEffect and onClick button do not work

App.js
import { Container, Row, Col } from "react-bootstrap";
import React, { useState, useEffect } from 'react';
import {person} from "./Data"
import DatesCounts from "./Components/DatesCounts";
import DatesList from "./Components/DatesList";
import DatesAction from "./Components/DatesAction";
function App() {
const [personData, setPersonData] = useState(person)
const onDelet =()=> {
setPersonData([])
}
const onViewData=() => {
setPersonData(person)
}
useEffect(()=>{
setPersonData([])
})
return (
<div className="color-body font">
<Container className="py-5">
<DatesCounts person={person} />
<DatesList person={person} />
<DatesAction deletData={onDelet} viewData={onViewData} />
</Container>
</div>
);
}
export default App;
DatesAction.js
import React from 'react';
import {Row,Col} from 'react-bootstrap';
const DatesAction = ({deletData , viewData}) => {
return (
<Row className=" justify-content-center my-2">
<Col sm="8" className="d-flex justify-content-between">
<button onClick={deletData} className="btn-style p-2">Clear All</button>
<button onClick={viewData} className="btn-style p-2">Show Data</button>
</Col>
</Row>
);
}
export default DatesAction;
I tried to execute useEffect to clear data in the beginning without success.
I also tried to execute onClick buttons Clear All and Show Data without success.
as you see the code is for Dates Reminder the componenets are working but the onClick buttons are not working also the useEffect doesn't work.
you need to send child components personData, instead of person. Since they are receiving the json instead of the useState information
<DatesCounts person={personData} />
<DatesList person={personData} />
To update the state try to use useEffect with dependency on your personData
useEffect(() => {}, [personData])
For setting the initial state, you can use use effect without dependency instead of setting it directly to useState.
useEffect(() => {
setPersonData(person);
}, [])
You don't need a hook especially when dealing with onclick events.
simply have a function which will reset the state and pass it as a prop to child component.
const initialState = { }; //definte initial state.
const [person, setPersons] = useState(initialState);
const resetState = () => {
setPersonState(initialState);
}
//render
<Child onReset={resetState} />
You are missing the dependency array in useEffect but anyway lets improve the performance of your code using React.memo and React.useCallback.
Wrap your function in a React.useCallback so that their reference will be same on next render and will improve some performance but to effectively use useCallback wrap the child component with React.memo
DatesAction.jsx
import React from "react";
import { Row, Col } from "react-bootstrap";
const DatesAction = React.memo(({ deletData, viewData }) => {
return (
<Row className=" justify-content-center my-2">
<Col sm="8" className="d-flex justify-content-between">
<button onClick={deletData} className="btn-style p-2">
Clear All
</button>
<button onClick={viewData} className="btn-style p-2">
Show Data
</button>
</Col>
</Row>
);
});
export default DatesAction;
App.jsx
import { Container, Row, Col } from "react-bootstrap";
import React, { useState, useEffect } from "react";
import { person } from "./Data";
import DatesCounts from "./Components/DatesCounts";
import DatesList from "./Components/DatesList";
import DatesAction from "./Components/DatesAction";
function App() {
const [personData, setPersonData] = useState(person);
const onDelet = React.useCallback(() => {
setPersonData([]);
}, [deps]);
const onViewData = React.useCallback(() => {
setPersonData(person);
}, [deps]);
useEffect(() => {
setPersonData([]);
}, [deps]);
return (
<div className="color-body font">
<Container className="py-5">
<DatesCounts person={person} />
<DatesList person={person} />
<DatesAction deletData={onDelet} viewData={onViewData} />
</Container>
</div>
);
}
export default App;

Netflix Clone Images won't show in rows

I'm newer to coding and have been creating a Netflix clone with react.
I'm trying to create rows and fetch the images into the rows with the tmdb API.
I have fetched the data into my console but I can't get the images to render on the UI.
I know its the src={} in my in my Row.jsx file.
Can anyone help me with fixing this please, I've tried following the documentation for tmdb and i still couldn't get it to work.
Thanks in advance :)
Row.jsx
import axios from 'axios';
import React, { useState, useEffect } from 'react';
const Row = ({ title, fetchURL }) => {
const [movies, setMovies] = useState([]);
useEffect(() => {
axios.get(fetchURL).then((response) => {
setMovies(response.data.results);
});
}, [fetchURL]);
console.log(movies);
return (
<>
<h2 className="text-white font-bold md:text-xl p-4">{title}</h2>
<div className="relative flex items-center">
<div id={'slider'}>
{movies?.map((item, id) => {
<div className="w-[160px] sm:w-[200px] md:w-[240px] lg:w-[280px] inline-block cursor-pointer relative p-2">
<img
src={`https://image.tmdb.org/t/p/original/${item?.backdrop_path}`}
alt={item?.title}
/>
</div>;
})}
</div>
</div>
</>
);
};
export default Row;
Requests.js
const requests = {
requestPopular: `https://api.themoviedb.org/3/movie/popular?api_key=${key}&language=en-US&page=1`,
requestTopRated: `https://api.themoviedb.org/3/movie/top_rated?api_key=${key}&language=en-US&page=1`,
requestTrending: `https://api.themoviedb.org/3/movie/popular?api_key=${key}&language=en-US&page=2`,
requestHorror: `https://api.themoviedb.org/3/search/movie?api_key=${key}&language=en-US&query=horror&page=1&include_adult=false`,
requestUpcoming: `https://api.themoviedb.org/3/movie/upcoming?api_key=${key}&language=en-US&page=1`,
};
export default requests;
Home.jsx
import React from 'react';
import Main from '../components/Main';
import Row from '../components/Row';
import requests from '../Requests';
const Home = () => {
return (
<>
<Main />
<Row title="Up Coming" fetchURL={requests.requestUpcoming} />
<Row title="Popular" fetchURL={requests.requestPopular} />
<Row title="Trending" fetchURL={requests.requestTrending} />
<Row title="Top Rated" fetchURL={requests.requestTopRated} />
<Row title="Horror" fetchURL={requests.requestHorror} />
</>
);
};
export default Home;

Error : "createSliderWithTooltip is not a function"

Im trying to implement the rc-slider for the web app however the rc-slider tooltip isnt recognized and shows an error of "createSliderWithTooltip is not a function" , which im not sure why .
For implementation of rc-slider i followed the rc-slider documentation which is the same way i have implemeneted in the code of home.js somehow im getting an error in console and nothing shows at all.
Thanks in advance.
Home.js
import React, { Fragment, useEffect , useState } from 'react'
import MetaData from './layouts/MetaData'
import { useDispatch , useSelector } from 'react-redux'
import { getProducts } from '../actions/products'
import Product from './products/Products'
import Loader from './layouts/Loader'
import { useAlert } from 'react-alert'
import Pagination from "react-js-pagination";
import {useParams} from 'react-router-dom'
import Slider from 'rc-slider'
import 'rc-slider/assets/index.css';
const { createSliderWithTooltip } = Slider;**//Error occurs here**
const Range = createSliderWithTooltip(Slider.Range)
const Home = () => {
const [currentPage,setCurrentPage]=useState(1);
const [price,setPrice]=useState([1,1000]);
let params=useParams();
const dispatch= useDispatch();
const alert=useAlert();
const {loading,products,error,productsCount,resPerPage,filteredProductsCount }= useSelector(state=>state.products)
const keyword=params.keyword;
useEffect(() => {
if (error) {
return alert.error("error");
}
dispatch(getProducts(keyword, currentPage));
}, [dispatch, alert, error, currentPage, keyword]);
function setCurrentPageNo(pageNumber) {
setCurrentPage(pageNumber)
}
return (
<Fragment>
{loading ? <Loader>Loading ...</Loader>:(
<Fragment>
<MetaData title={'Buy Electronics , Household Items and Many Others Online'} />
<h1 id="products_heading">Latest Products</h1>
<section id="products" className="container mt-5">
<div className="row">
<Fragment>
<div className="col-6 col-md-3 mt-5 mb-5">
<div className="px-5">
<Range
marks={{
1: `$1`,
1000: `$1000`
}}
min={1}
max={1000}
defaultValue={[1, 1000]}
tipFormatter={value => `$${value}`}
tipProps={{
placement: "top",
visible: true
}}
value={price}
onChange={price => setPrice(price)}
/>
</div>
</div>
</Fragment>
{products.map(product => (
<Product key={product._id} product={product} col={4} />
))}
</div>
</section>
<div className="d-flex justify-content-center mt-5">
<Pagination
activePage={currentPage}
itemsCountPerPage={resPerPage}
totalItemsCount={productsCount}
onChange={setCurrentPageNo}//sets current page no as it changes for state management
nextPageText={'Next'}
prevPageText={'Prev'}
itemClass="page-item"
linkClass="page-link"
/>
</div>
</Fragment>
)
}
</Fragment>
)}
export default Home
Instead of const { createSliderWithTooltip } = Slider;, try this:
const createSliderWithTooltip = Slider.createSliderWithTooltip;
I tried several ways and the only thing that actually worked was downgrading to 9.6.5 rc-slider and now everything is working perfectly
The document hasn't been updated yet since new version. As it seems you want to use Range component, now here the way to do it (thanks to Ashvin-Pal): https://github.com/react-component/slider/issues/825#issuecomment-1084416952
The createSliderWithTooltip has been removed in the new version.
Instead, you can implement your custom handle or tooltip easily like this:
handleRender={renderProps => {
return (
<div {...renderProps.props}>
<SliderTooltip>{round}%</SliderTooltip>
</div>
);
}}
let me know if you have any questions.

issue with material ui components responsiveness

I am trying to use matrial ui elements,but my issue is that it is not responsive. I want to show 2
items on medium screen and just 1 on small ones, but it is 3 on all screens. thx
this is the parent component and I prop drill the array's elements to the child component
import * as React from "react";
import Box from "#mui/material/Box";
import ImageList from "#mui/material/ImageList";
import ImageListItem from "#mui/material/ImageListItem";
import ImageListItemBar from "#mui/material/ImageListItemBar";
import InfiniteScroll from "react-infinite-scroll-component";
import Grid from "#mui/material/Grid";
import ProductListItem from "./ProductListItem";
const ProductList = ({ products }) => {
return (
<>
<div className="mr-5 ml-5">
<Box>
<ImageList variant="masonry" cols={3} gap={18}>
{products.map((item) => (
<ProductListItem
item={item}
key={item.id}
/>
))}
</ImageList>
</Box>
</div>
</>
);
};
export default ProductList;
this is the child component for each element of array
import React, { useState, useEffect } from "react";
import Heart from "react-heart";
import { useLocalStorage } from "./storage.js";
import ImageListItem from "#mui/material/ImageListItem";
import ImageListItemBar from "#mui/material/ImageListItemBar";
import ImageList from "#mui/material/ImageList";
const ProductListItem = (props) => {
return (
<>
<ImageListItem>
<img
src={`${props.item.img_src}?w=248&fit=crop&auto=format`}
srcSet={`${props.item.img_src}?w=248&fit=crop&auto=format&dpr=2 2x`}
alt={props.item.title}
loading="lazy"
/>
<div style={{ width: "2rem" }} className="ml-2 mt-2">
<Heart />
</div>
<ImageListItemBar
subtitle={<span>by: aaaaaaaaaaaa</span>}
position="below"
align="right"
/>
</ImageListItem>
</>
);
};
export default ProductListItem;

Uncaught TypeError: Cannot read properties of undefined (reading 'pathname')

When clicking on a button, the system raises the following error, rather than navigating to the ProductDetail component:
Uncaught TypeError: Cannot read properties of undefined (reading 'pathname')
The product id in the url is correctly identified, and if I type the url manually, I get the corresponding REST API view, but trying to navigate there through the button does not work.
Any ideas of what am I doing wrong?
Here what I use:
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.1",
App.js
import React, { Component } from "react";
import { render } from "react-dom";
import Home from "./Home";
import Header from "./Header";
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom';
import ShowProducts3 from './ShowProducts3';
import ProductDetail from './ProductDetail';
function App() {
return (
<div className="min-h-screen bg-red-700 text-gray-900">
<div>
<Router >
<Header />
<Routes>
<Route exact path="/" element={<ShowProducts3 />} />
<Route exact path="/api/pp/:id" element={<ProductDetail />} />
</Routes>
</Router>
</div>
</div>
);
}
export default App;
ShowProducts3.js
import axios from 'axios';
import React, {useState, useEffect} from 'react';
import Card from '#mui/material/Card';
import { CardActionArea } from '#mui/material';
import CardActions from '#mui/material/CardActions';
import CardContent from '#mui/material/CardContent';
import Button from '#mui/material/Button';
import Typography from '#mui/material/Typography';
import { Link } from 'react-router-dom';
import Container from '#mui/material/Container';
import Grid from '#mui/material/Grid';
const API_URL = "http://localhost:8000/api/pp/"
const ShowProducts3 = () => {
const [products, setProducts] = useState([])
const fetchProducts = async () => {
const result = await axios.get(API_URL);
console.log(result.data)
setProducts(result.data)
}
useEffect(() => {
fetchProducts();
},[])
const goToDetail = () => {
alert("detail page")
}
return (
<div>
<div className="main-products-show">
<Container>
<Grid container spacing={{ xs: 2, md: 3 }} >
{
products.map((product) => (
<Grid item xs={2} sm={4} md={4} key={product.pk}>
<Card key={product.pk} sx={{ minWidth: 155 }}>
<CardActionArea>
<CardContent>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
{product.name}
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
{product.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button className="btn btn-secondary mr-2" component={Link} to={`/api/pp/${product.pk}`} size="small">Details</Button>
</CardActions>
</Card>
</Grid>
))
}
</Grid>
</Container>
</div>
</div>
);
};
export default ShowProducts3;
enter image description here
EDIT
The error seems to be linked to the "Delete" button within component "ProductDetail". If I remove this line, the error disappears.
deleteProduct(product.pk)}>Delete
Any idea what's wrong with it?
import axios from 'axios';
import React, {useState, useEffect} from 'react';
import { useParams, useNavigate } from 'react-router';
import { Link } from 'react-router-dom';
const ProductDetail = () => {
const [product, setProduct] = useState([])
const {id} = useParams();
const history = useNavigate();
useEffect(() => {
getSingleProduct();
return () => {
setProduct({});
};
},[])
const API_URL = "http://localhost:8000/api/pp/"
const getSingleProduct = async () => {
const { data } = await axios.put(`http://localhost:8000/api/pp/${id}/`)
console.log(data);
setProduct(data);
}
const deleteProduct = async (id) => {
await axios.delete(`http://localhost:8000/api/pp/${id}/`)
history.push("/")
}
return (
<div>
<h2>Detail of Single Product </h2>
<hr></hr>
<div className="full-product-detail">
<div className="product-detail">
<p>{product.pk}</p>
<p>{product.name}</p>
<p>{product.description}</p>
</div>
</div>
<Link className="btn btn-outline-primary mr-2" to={`/${product.pk}/update`}>Update</Link>
<Link className="btn btn-danger" onClick={() => deleteProduct(product.pk)}>Delete</Link>
</div>
);
};
export default ProductDetail;
Maybe the problem related to your link and button component try using this:
import { Link } from '#mui/material/Link';
<Button component={Link} variant="contained" href={`/api/pp/${product.pk}`}>
Link
</Button>

Resources