How to match an element with data clicked in react hooks? - reactjs

I am trying to create a modal that will display all the data on the element clicked.
For exemple, if i clicked the id(367) of a movie, it will display the title, the genres and the released date.

First i created a Context.js that will send data in the component Movie.js:
Context.js
import React, { useState, useEffect, createContext } from "react";
import { getAxios } from "../Function/index";
const MovieContext = createContext();
const MovieProvider = ({ children }) => {
const [datas, setDatas] = useState([]);
const [testdatas, testsetDatas] = useState([]);
const getMovie = async () => {
const data = await getAxios(
"https://api.themoviedb.org/3/movie/upcoming?api_key={}"
);
setDatas(data.results);
};
useEffect(() => {
getMovie();
}, []);
return (
<MovieContext.Provider value={{ datas, testdatas }}>{children}</MovieContext.Provider>
);
};
export { MovieContext, MovieProvider };
Movie.js
import React, { useContext } from "react";
import { MovieContext } from '../Context'
import { Card, Row, Tag } from 'antd'
const Main = () => {
const { datas } = useContext(MovieContext)
return (
<Row gutter={16}>
{datas.map((item, id) => (
<Card
key={id}
style={{ width: 300, margin: 10 }} bordered={true}
hoverable
>
<div>
<p style={{ textAlign: "left" }}>{item.title} <span style={{ float: "right" }}>{item.vote_average}</span></p>
</div>
<p><img src={`https://image.tmdb.org/t/p/w500/${item.poster_path}`} alt="#" width="200" height="200" /></p>
<Tag color="red"> {item.title}</Tag>
</Card>
))}
</Row >
);
};
export default Main;

Related

multiple fetch requests using List ( 6 times after compiling, multiple while scrolling )

im new to react and im building an app where i encountered this problem, i have a component that fetches data and displays it in a list ,but on the page, it fetches multiple times each time i scroll the list.`
import axios from 'axios';
import React, {useState, useEffect} from 'react'
import Box from '#mui/material/Box';
import ListItem from '#mui/material/ListItem';
import ListItemButton from '#mui/material/ListItemButton';
import { FixedSizeList} from 'react-window';
import ListItemText from '#mui/material/ListItemText';
import { useNavigate } from "react-router-dom";
const JobsListData = (props) => {
const navigate = useNavigate();
var { index, style } = props;
const url = 'http://localhost:5000/api/v1/jobs'
const [jobsList,setJobs] = useState(null)
const jobsfunction = async () => {
try{
const data = await axios.get(url)
.then(response => {
setJobs(response.data)
})
}catch(e){
console.log(e);
}
}
useEffect(()=>{
jobsfunction();
},[]);
if(jobsList){
const maxlength = jobsList.total_results;
if(index<maxlength){return (
<ListItem key={index} component="div" disablePadding>
<ListItem >
<p><h3>{jobsList.jobs[index].name}</h3><br></br>
<b>Payrate</b> :{jobsList.jobs[index].payrate}<br></br>
<b>Adress</b> :{jobsList.jobs[index].adress}<br></br>
<b>Job ID</b> :{jobsList.jobs[index]._id}</p>
</ListItem>
</ListItem>
);}}
return ([])
}
function VirtualizedList() {
return (
<div className='VirtualizedList' id='VirtualizedList'>
<div className='container'>
<div className='col-2'>
<Box
sx={{ width: '100%', height: 400, maxWidth: 360, bgcolor: 'background.paper' }}
>
<FixedSizeList
height={400}
width={650}
itemSize={100}
itemCount={200}
>
{JobsListData}
</FixedSizeList>
</Box>
</div>
</div>
</div>
);
}
export default VirtualizedList;
`
when i remove the FixedSizeList the fetch request happens only once but when it is there, it fetches (6 times to be exact) in the beginning except for the scroll thing.
this problem causes the list to glitch out and sometimes disappear
try to fetch the data in the parent component
Something like that:
import axios from 'axios';
import React, {useState, useEffect} from 'react'
import Box from '#mui/material/Box';
import ListItem from '#mui/material/ListItem';
import ListItemButton from '#mui/material/ListItemButton';
import { FixedSizeList} from 'react-window';
import ListItemText from '#mui/material/ListItemText';
import { useNavigate } from "react-router-dom";
const JobsListData = (props) => {
const navigate = useNavigate();
var { index, style, jobsList } = props;
if(jobsList){
const maxlength = jobsList.total_results;
if(index<maxlength){return (
<ListItem key={index} component="div" disablePadding>
<ListItem >
<p><h3>{jobsList.jobs[index].name}</h3><br></br>
<b>Payrate</b> :{jobsList.jobs[index].payrate}<br></br>
<b>Adress</b> :{jobsList.jobs[index].adress}<br></br>
<b>Job ID</b> :{jobsList.jobs[index]._id}</p>
</ListItem>
</ListItem>
);}}
return ([])
}
function VirtualizedList() {
const url = 'http://localhost:5000/api/v1/jobs'
const [jobsList,setJobs] = useState(null)
const jobsfunction = async () => {
try{
const data = await axios.get(url)
.then(response => {
setJobs(response.data)
})
}catch(e){
console.log(e);
}
}
useEffect(()=>{
jobsfunction();
},[]);
return (
<div className='VirtualizedList' id='VirtualizedList'>
<div className='container'>
<div className='col-2'>
<Box
sx={{ width: '100%', height: 400, maxWidth: 360, bgcolor: 'background.paper' }}
>
<FixedSizeList
height={400}
width={650}
itemSize={100}
itemCount={200}
>
<JobsListData jobsList={jobsList}/>
</FixedSizeList>
</Box>
</div>
</div>
</div>
);
}
export default VirtualizedList;

How to update Draft Value when click of Save Button?

I have a two Input form and a Paragraph, when I try to change the value of input the paragraph get updated, once the paragraph is updated I am trying to edit the paragraph with the help of drafts library, but once I update the paragraph and save it, it doesn't update the paragraph.
Please anyone Help me out to solve the problem
Codesandbox Link : Code
Context API
import React, { useState, createContext } from "react";
export const Contx = createContext();
export const ConProvider = ({ children }) => {
const [customerName, setCustomerName] = useState("");
const [amount, setAmount] = useState("");
const defaultValue = `<p>Hello ${
customerName === "" ? "User" : customerName
},</p>
<p>Please Click on the link below to pay the order - <strong>${amount}</strong> . </p>
<p>Click hear to pay</p>
<br/>
<p>Thanks and Regards</p>
<p>testUser</p>`;
const [draftValue, setDraftValue] = useState(defaultValue);
return (
<Contx.Provider
value={{
defaultValue,
setCustomerName,
setAmount,
customerName,
amount,
setDraftValue,
draftValue
}}
>
{children}
</Contx.Provider>
);
};
homePage
import React, { useContext, useState } from "react";
import ReactDOM from "react-dom";
import { ConProvider, Contx } from "../ContextApi";
import Data from "./Component/Data/Data";
import NewDraft from "./Component/Data/NewDraft";
import Modal from "./Component/Data/Modal";
import "./styles.css";
function App() {
const { defaultValue, setDraftValue, draftValue } = useContext(Contx);
// console.log("defaultValue", defaultValue);
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="App">
<Data />
<Modal handleClose={handleClose} show={show}>
<NewDraft
prsFunc={setDraftValue}
handleClose={handleClose}
defaultValueEmpty={false}
defaultValue={defaultValue}
/>
</Modal>
<div
className="templateStyle p-2"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: draftValue && draftValue
}}
/>
<button onClick={handleShow}>Edit</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<ConProvider>
<App />
</ConProvider>,
rootElement
);
Input Form
import React, { useContext } from "react";
import { Contx } from "../../../ContextApi";
export default function Data() {
const {
setCustomerName,
setDraftValue,
defaultValue,
setAmount,
customerName,
amount
} = useContext(Contx);
React.useEffect(() => {
setDraftValue(defaultValue);
});
// console.log("fffffff", customerName, amount);
return (
<div>
<input
type="text"
value={customerName}
name="customerName"
placeholder="Enter Customer name"
onChange={(e) => {
setCustomerName(e.target.value);
}}
/>
<input
type="number"
placeholder="Enter Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
);
}
DraftJS
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import {
EditorState,
convertToRaw,
ContentState,
convertFromHTML
} from "draft-js";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
import draftToHtml from "draftjs-to-html";
import "./Data.css";
export default function NewDraft({
handleClose,
defaultValue,
defaultValueEmpty,
prsFunc
}) {
const initialState = defaultValueEmpty
? () => EditorState.createEmpty()
: EditorState.createWithContent(
ContentState.createFromBlockArray(convertFromHTML(defaultValue))
);
const [editorState, setEditorState] = useState(initialState);
const onChange = (value) => {
setEditorState(value);
};
const saveData = () => {
prsFunc(draftToHtml(convertToRaw(editorState.getCurrentContent())));
handleClose();
};
// console.log(draftToHtml(convertToRaw(editorState.getCurrentContent())));
return (
<div>
<div style={{ border: "2px solid", padding: "20px" }}>
<Editor
editorState={editorState}
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
onEditorStateChange={(value) => onChange(value)}
/>
<button variant="secondary" onClick={saveData}>
Save
</button>
</div>
</div>
);
}
The problem you are facing is caused by this line in Data component. Every time the component is updated, the draftValue is set to the defaultValue.
React.useEffect(() => {
setDraftValue(defaultValue);
});

I have a search bar that wont update whenever I press backspace

So I have a basic messenger chap application, the search bar to retrieve chats works fine whenever I type in letters, yet whenever I backspace, the event handler wont update, and I am left with the chat room lists that fit the search, even though the search bar is left empty.
Examples below
1.All chatrooms are shown below:
2.Typing "funny" filters a list with chatrooms labeled "funny":
3.Pressing backspace does not show all the chatrooms:
import { Avatar, IconButton } from '#material-ui/core';
import React, { useEffect, useState } from 'react';
import './Sidebar.css';
import SearchIcon from '#material-ui/icons/Search';
import { RateReviewOutlined } from '#material-ui/icons';
import { SidebarChat } from './SidebarChat';
import { useSelector } from 'react-redux';
import { selectUser } from './features/userSlice';
import db, { auth } from './firebase';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import Backdrop from '#material-ui/core/Backdrop';
import Fade from '#material-ui/core/Fade';
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
export function Sidebar(props) {
const user = useSelector(selectUser);
const [chats, setChats] = useState([]);
const classes = useStyles();
const [open, setOpen] = useState(false);
const [search, setSearch] = useState('');
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
useEffect(() => {
db.collection('chats').onSnapshot((snapshot) =>
setChats(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
)
);
}, []);
const addChat = () => {
const chatName = prompt('Please enter a chat name');
if (chatName) {
db.collection('chats').add({
chatName: chatName,
});
}
};
const searchFunction = (e) => {
setSearch(e);
console.log(search);
console.log(chats);
const filtered = chats.filter(chat => {
return chat.data.chatName.toLowerCase().includes(e.toLowerCase())
});
// console.log(filtered)
setChats(filtered);
};
return (
<div className="sidebar">
<div className="sidebar__header">
<Avatar
onClick={handleOpen}
src={user.photo}
className="sidebar__avatar"
/>
<div className="sidebar__input">
<SearchIcon />
<input
placeholder="Search"
value={search}
onChange={(e) => searchFunction(e.target.value)}
/>
</div>
<IconButton variant="outlined" className="sidebar__inputButton">
<RateReviewOutlined onClick={addChat} />
</IconButton>
</div>
<div className="sidebar__chats">
{chats.map(({ id, data: { chatName } }) => (
<SidebarChat key={id} id={id} chatName={chatName} />
))}
</div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">
Are you sure you want to sign out?
</h2>
<button onClick={() => auth.signOut()}>Yes</button>
<button onClick={handleClose}>No</button>
</div>
</Fade>
</Modal>
</div>
);
}
In your searchFunction it is constantly updating filtered to have less and less chatrooms. You could store chat rooms as a separate state and then filter that instead to preserve all chat rooms.

I have a simple messenger app with a search bar that wont update the filter whenever I press backspace

So I have a basic messenger chat application with a search bar to retrieve chatrooms, and it works fine whenever I type in letters, yet whenever I backspace, why wont it retrieve the list I had previous?
Examples below
1.All chatrooms are shown below:
2.Typing "funny" filters a list with chatrooms labeled "funny":
3.Pressing backspace does not show all the chatrooms:
import { Avatar, IconButton } from '#material-ui/core';
import React, { useEffect, useState } from 'react';
import './Sidebar.css';
import SearchIcon from '#material-ui/icons/Search';
import { RateReviewOutlined } from '#material-ui/icons';
import { SidebarChat } from './SidebarChat';
import { useSelector } from 'react-redux';
import { selectUser } from './features/userSlice';
import db, { auth } from './firebase';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import Backdrop from '#material-ui/core/Backdrop';
import Fade from '#material-ui/core/Fade';
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
export function Sidebar(props) {
const user = useSelector(selectUser);
const [chats, setChats] = useState([]);
const classes = useStyles();
const [open, setOpen] = useState(false);
const [search, setSearch] = useState('');
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
useEffect(() => {
db.collection('chats').onSnapshot((snapshot) =>
setChats(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
)
);
}, []);
const addChat = () => {
const chatName = prompt('Please enter a chat name');
if (chatName) {
db.collection('chats').add({
chatName: chatName,
});
}
};
const searchFunction = (e) => {
setSearch(e);
console.log(search);
console.log(chats);
const filtered = chats.filter(chat => {
return chat.data.chatName.toLowerCase().includes(e.toLowerCase())
});
// console.log(filtered)
setChats(filtered);
};
return (
<div className="sidebar">
<div className="sidebar__header">
<Avatar
onClick={handleOpen}
src={user.photo}
className="sidebar__avatar"
/>
<div className="sidebar__input">
<SearchIcon />
<input
placeholder="Search"
value={search}
onChange={(e) => searchFunction(e.target.value)}
/>
</div>
<IconButton variant="outlined" className="sidebar__inputButton">
<RateReviewOutlined onClick={addChat} />
</IconButton>
</div>
<div className="sidebar__chats">
{chats.map(({ id, data: { chatName } }) => (
<SidebarChat key={id} id={id} chatName={chatName} />
))}
</div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">
Are you sure you want to sign out?
</h2>
<button onClick={() => auth.signOut()}>Yes</button>
<button onClick={handleClose}>No</button>
</div>
</Fade>
</Modal>
</div>
);
}
You need to save the initial list of chats in a variable and once the search is set to blank '' you need to update your chats with the initial saved chats.
Problem here is -
const filtered = chats.filter(chat => {
return chat.data.chatName.toLowerCase().includes(e.toLowerCase())
});
// console.log(filtered)
setChats(filtered);

document.querySelector() always return null when clicking on React Router Link the first time but will return correctly after

I'm stucking on a problem with React-Router and querySelector.
I have a Navbar component which contains all the CustomLink components for navigation and a line animation which listens to those components and displays animation according to the current active component.
// Navbar.tsx
import React, { useCallback, useEffect, useState, useRef } from "react";
import { Link, useLocation } from "react-router-dom";
import CustomLink from "./Link";
const Layout: React.FC = ({ children }) => {
const location = useLocation();
const navbarRef = useRef<HTMLDivElement>(null);
const [pos, setPos] = useState({ left: 0, width: 0 });
const handleActiveLine = useCallback((): void => {
if (navbarRef && navbarRef.current) {
const activeNavbarLink = navbarRef.current.querySelector<HTMLElement>(
".tdp-navbar__item.active"
);
console.log(activeNavbarLink);
if (activeNavbarLink) {
setPos({
left: activeNavbarLink.offsetLeft,
width: activeNavbarLink.offsetWidth,
});
}
}
}, []);
useEffect(() => {
handleActiveLine();
}, [location]);
return (
<>
<div className="tdp-navbar-content shadow">
<div ref={navbarRef} className="tdp-navbar">
<div className="tdp-navbar__left">
<p>Todo+</p>
<CustomLink to="/">About</CustomLink>
<CustomLink to="/login">Login</CustomLink>
</div>
<div className="tdp-navbar__right">
<button className="tdp-button tdp-button--primary tdp-button--border">
<div className="tdp-button__content">
<Link to="/register">Register</Link>
</div>
</button>
<button className="tdp-button tdp-button--primary tdp-button--default">
<div className="tdp-button__content">
<Link to="/login">Login</Link>
</div>
</button>
</div>
<div
className="tdp-navbar__line"
style={{ left: pos.left, width: pos.width }}
/>
</div>
</div>
<main className="page">{children}</main>
</>
);
};
export default Layout;
// CustomLink.tsx
import React, { useEffect, useState } from "react";
import { useLocation, useHistory } from "react-router-dom";
interface Props {
to: string;
}
const CustomLink: React.FC<Props> = ({ to, children }) => {
const location = useLocation();
const history = useHistory();
const [active, setActive] = useState(false);
useEffect(() => {
if (location.pathname === to) {
setActive(true);
} else {
setActive(false);
}
}, [location, to]);
return (
// eslint-disable-next-line react/button-has-type
<button
className={`tdp-navbar__item ${active ? "active" : ""}`}
onClick={(): void => {
history.push(to);
}}
>
{children}
</button>
);
};
export default CustomLink;
But it doesn't work as I want. So I opened Chrome Devtool and debugged, I realized that when I clicked on a CustomLink first, the querySelector() from Navbar would return null. But if I clicked on the same CustomLink multiple times, it would return properly, like the screenshot below:
Error from Chrome Console
How can I get the correct return from querySelector() from the first time? Thank you!
It's because handleActiveLine will trigger before setActive(true) of CustomLink.tsx
Add a callback in CustomLink.tsx:
const CustomLink: React.FC<Props> = ({ onActive }) => {
useEffect(() => {
if (active) {
onActive();
}
}, [active]);
}
In Navbar.tsx:
const Layout: React.FC = ({ children }) => {
function handleOnActive() {
// do your query selector here
}
// add onActive={handleOnActive} to each CustomLink
return <CustomLink onActive={handleOnActive} />
}

Resources