Im building this app for a shop, I have the Login already working but I want that when Login in app, it shows the Sidebar, and when choosing an option, the Sidebar may not dissapear only the content part should change, but it doesn't, heres what I have:
App.js
import React from 'react';
import Login from './components/Login'
import Dashboard from './components/Dashboard';
import Administrar from './components/Productos/Administrar';
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom'
function App() {
return (
<React.Fragment>
<Router>
<Switch>
<Route path="/" exact render={ props => (<Login {...props} />)}></Route>
<Route path="/dashboard" exact render={ props => (<Dashboard {...props} />)}></Route>
</Switch>
</Router>
</React.Fragment>
);
}
export default App;
Dashboard.jsx
import React, { useState, useEffect} from 'react';
import Sidebar from '../template/Sidebar';
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import axios from 'axios';
import {ApiUrl} from '../services/apirest'
import Administrar from './Productos/Administrar'
const SideAndNavbar = () => {
return (
<React.Fragment>
<Sidebar/>
<Router>
<Switch>
<Route path="/productos/administrar" exact component={ props => (<Administrar {...props} />)}></Route>
</Switch>
</Router>
</React.Fragment>
)
}
const Relog = () => {
return (
<div>
<h1>Relogeate pá</h1>
</div>
)
}
export default function Dashboard() {
const [user, setUser] = useState(null)
useEffect(() => {
obtenerUsuario()
}, [])
const obtenerUsuario = async () => {
let url = ApiUrl + "/usuario";
await axios.get(url)
.then(response => {
setUser(response.data.user)
}).catch(error => {
console.log(error)
})
}
return (
(user ? <SideAndNavbar/>: <Relog/>)
);
}
Login.jsx
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css'
import toast, {Toaster} from 'react-hot-toast'
import logo from '../assets/img/img-01.png'
import axios from 'axios'
import {ApiUrl} from '../services/apirest'
class Login extends Component {
// eslint-disable-next-line
constructor(props){
super(props);
}
state = {
form:{
"email": "",
"password": ""
},
}
manejadorChange = async(e) =>{
await this.setState({
form: {
...this.state.form,
[e.target.name]: e.target.value
}
})
}
manejadorBoton = () => {
let url = ApiUrl + "/auth/logearse";
axios.post(url, this.state.form)
.then(response => {
if(response.data.status === "OK"){
localStorage.setItem("token", response.data.token);
/*
this.props.history.push({
pathname: '/dashboard',
state: response.data.user
})
*/
this.props.history.push('/dashboard');
}else{
toast.error(response.data.message);
}
}).catch(error => {
console.log(error);
toast.error("Error al conectarse con el servidor");
})
}
manejadorSubmit(e){
e.preventDefault();
}
render() {
return (
<React.Fragment>
<Toaster position="top-center" reverseOrder={false}/>
<div className="limiter">
<div className="container-login100">
<div className="wrap-login100">
<div className="login100-pic">
<img src={logo} alt="Imagen"/>
</div>
<form className="login100-form validate-form" onSubmit={this.manejadorSubmit}>
<span className="login100-form-title">
Ingreso de Usuario
</span>
<div className="wrap-input100 validate-input" data-validate = "Valid email is required: ex#abc.xyz">
<input className="input100" type="text" name="email" placeholder="Email" onChange={this.manejadorChange}/>
<span className="focus-input100"></span>
<span className="symbol-input100">
<i className="fa fa-envelope" aria-hidden="true"></i>
</span>
</div>
<div className="wrap-input100 validate-input" data-validate = "Password is required">
<input className="input100" type="password" name="password" placeholder="Password" onChange={this.manejadorChange}/>
<span className="focus-input100"></span>
<span className="symbol-input100">
<i className="fa fa-lock" aria-hidden="true"></i>
</span>
</div>
<div className="container-login100-form-btn">
<button className="login100-form-btn" onClick={this.manejadorBoton}>
Ingresar
</button>
</div>
<div className="text-center p-t-56">
</div>
</form>
</div>
</div>
</div>
</React.Fragment>
);
}
}
export default Login;
What can I do? Here is some pics:
enter image description here
Looks like you are trying to achieve some heavy Routing without the help of any state management, There are few anti patterns in your current setup. I have a few suggestions that will help you achieve what you are trying to implement.
Firstly Your application routing is setup in a ambiguous way. If you see the routing implementation looks like a nested Routing setup. but the URl you are used is /productos/administrar this route in terms of relativity is rendered from the / base URL.
Your Home(Entry point of the application APP.js) is setup with a parent Router, The router now reads your URL and Tries to render a component. but sees that in the parent Route there is no such URL.
Now if you see carefully your dashboard component is setup to render <SideNav>, to render the sidenav on the /productos/administrar route you should firstly go through the dashboard component.
in your current setup the dashboard component is missed and directly the Admin component is rendered at the root of the router.
I would want you to follow the Layout Pattern where you can stuff the sidenav and topnav (if you have any) along with a main section which render the childrens passed to the component, and on each route call this Layout Component with children props.
this way you ensure the Layout is visible on every route. But that's a long way . If you want a quick fix is to implement proper nested Routing using useRouterMatch() to pass the current route down the component tree. Now change the dashboard component to something like this
const SideAndNavbar = ({match}) => {
return (
<React.Fragment>
// Make sure you user the match props in Sidebar to append he correct URl for nesting to the Link tag , for example (<Link to={`${match.url}/productos/administrar`}>GO to Admin</Link>)
<Sidebar match={match}/>
<Router>
<Switch>
<Route path={`${match.path}/productos/administrar`} exact component={ props => (<Administrar {...props} />)}></Route>
</Switch>
</Router>
</React.Fragment>
)
}
const Relog = () => {
return (
<div>
<h1>Relogeate pá</h1>
</div>
)
}
export default function Dashboard() {
const [user, setUser] = useState(null)
let match = useRouteMatch();
useEffect(() => {
obtenerUsuario()
}, [])
const obtenerUsuario = async () => {
let url = ApiUrl + "/usuario";
await axios.get(url)
.then(response => {
setUser(response.data.user)
}).catch(error => {
console.log(error)
})
}
return (
(user ? <SideAndNavbar match={match} />: <Relog/>)
);
}
For more info on this topic see the official documentation of React Router
Related
I'm using React.js and firebase to build a web app. Using firebase, when the auth state changes as the user signs in with google, the pages is supposed to be redirected to the "/home" route. instead, it signs in and then "freezes". The page becomes unresponsive. I have checked the console logs on the localhost:3000 page. It keeps throwing a warning multiple times. see below
Kindly help me fix this.
Throttling navigation to prevent the browser from hanging. See https://crbug.com/1038223. Command line switch --disable-ipc-flooding-protection can be used to bypass the protection
below is my code.
//Header.js component
import { signInWithPopup } from "firebase/auth";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import { selectUserEmail, selectUserName, selectUserPhoto, setSignOutState, setUserLoginDetails } from "../features/user/userSlice";
import { auth, provider } from "../firebase";
//header component.
const Header = (props) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const userEmail = useSelector(selectUserEmail);
const userName = useSelector(selectUserName);
const userPhoto = useSelector(selectUserPhoto);
// moniter auth state. if user signs in , redirect him to homepage
useEffect(() => {
auth.onAuthStateChanged(
async (user) => {
if (user) {
setUser(user);
navigate("/home");
}
},
[userName, userEmail, userPhoto]
);
});
//handle auth ftn
const handleAuth = async () => {
provider.addScope("profile");
provider.addScope("email");
if (!userName) {
const result = await signInWithPopup(auth, provider);
setUser(result.user).catch((error) => {
alert(error.message);
});
console.log(result);
} else if (userName) {
auth
.signOut()
.then(() => {
dispatch(setSignOutState());
navigate("/");
})
.catch((error) => {
alert(error.message);
});
}
};
//dispatch ftn
const setUser = (user) => {
dispatch(
setUserLoginDetails({
name: user.displayName,
email: user.email,
photo: user.photoURL,
})
);
};
//UI of the COMPONENT
return (
<Nav>
<Logo>
<img src="/images/logo.svg" alt="Disney Logo" />
</Logo>
{!userName ? (
<Login onClick={handleAuth}>LOGIN</Login>
) : (
<>
<NavMenu>
<a href="/home">
<img src="/images/home-icon.svg" alt="home" />
<span>HOME</span>
</a>
<a href="/search">
<img src="/images/search-icon.svg" alt="home" />
<span>SEARCH</span>
</a>
<a href="/watchlist">
<img src="/images/watchlist-icon.svg" alt="home" />
<span>WATCHLIST</span>
</a>
<a href="/original">
<img src="/images/original-icon.svg" alt="home" />
<span>ORIGINALS</span>
</a>
<a href="/movies">
<img src="/images/movie-icon.svg" alt="home" />
<span>MOVIES</span>
</a>
<a href="/series">
<img src="/images/series-icon.svg" alt="home" />
<span>SERIES</span>
</a>
</NavMenu>
<Signout>
<UserImg src={userPhoto} alt={userName} />
<DropDown>
<span onClick={handleAuth}>Sign out</span>
</DropDown>
</Signout>
</>
)}
</Nav>
);
};
//App.js code
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import "./App.css";
import Header from "./components/Header";
import Home from "./components/Home";
import Login from "./components/Login";
function App() {
return (
<div className="App">
<Router>
<Header />
<Routes>
<Route path="/" element={<Login />}></Route>
<Route path="/home" element={<Home />}></Route>
</Routes>
</Router>
</div>
);
}
export default App;
useEffect(() => {
auth.onAuthStateChanged(async (user) => {
if (user) {
setUser(user);
navigate("/home");
}
});
}, [userName]);
just added userName as a dependency and it fixed it. The problem was that without the dependency, it was throwing an infinite loop.
Main.js
import axios from 'axios'
import React, { Component, useEffect, useState } from 'react'
import { BrowserRouter as Router, Switch, Route} from "react-router-dom";
import Books from './Books'
import Navbar from './Navbar'
import AddBook from './AddBook';
import BookDetail from './BookDetail';
const Main = () => {
let [selectedBook, setSelectedBook] = useState() // Storing the data from Books.js
let [books, setBooks] = useState([])
useEffect(() => {
axios.get('http://127.0.0.1:8000/api/get-books')
.then(res => {
setBooks(books = res.data)
})
})
return (
<Router>
<div>
<Navbar title={selectedBook}/> // is defined here <----
<div className='container'>
<Switch>
<Route exact path='/' >
<Books books={books} onBookSelect={(book) => {setSelectedBook(book)} }/> // Receiving the data from here
</Route>
<Route path='/add-book' >
<AddBook />
</Route>
<Route path={`/details`} >
<BookDetail bookId={selectedBook} /> // is undefined when passed here <----
</Route>
</Switch>
</div>
</div>
</Router>
)
}
export default Main
Books.js
import React, { Component, useState } from 'react'
import BookDetail from './BookDetail'
const Books = ({books, onBookSelect}) => {
const sendBookId = e => {
let bookId = e.target.value
onBookSelect('i am not undefined') // sending the data to Main.js
setTimeout(function(){ window.location.href='http://localhost:3000/details' }, 10)
}
return (
<div>
<div className='books'>
{books.map(book => (
<div className='book'>
<h2>{book.name}</h2>
<p>Author: <a href='#'>{book.author}</a></p>
<button className="btn" value={book.id} onClick={sendBookId}>View Details</button> // Getting the id from here
</div>
))}
</div>
</div>
)
}
export default Books
BookDetail.js
import React, { useEffect } from 'react'
import axios from 'axios'
import { useState } from 'react'
const BookDetail = ({bookId}) => {
return (
<div>
<p>book: {bookId}</p> // bookId is undefined
</div>
)
}
export default BookDetail
Sample from localhost:8000/api/get-books
{
"id": 5,
"name": "Book1",
"author": "Author1",
"content": "a normal book",
"isbn": "235456",
"passcode": "123",
"created_at": "2021-07-12T16:29:47.114356Z",
"pages": "3"
}
Basically, the data is sent from Book.js to the parent component which is Main.js and which is stored in selectedBook, and the data is defined and displays in the title, but when I add it as a prop in and try to access it from there it becomes undefined, what am I doing wrong?
Issue
Credit to Yoshi for calling it out first, but there is an issue with the way you navigate from your home page to the book details page. Mutating the window.location.href object will actually reload the page, and your app. Since React state lives in memory it is lost upon page reloading.
Solution
Use the history object provided by the Router context to PUSH to the new route. Since you are already using a function component you can import the useHistory React hook from react-router-dom and issue the imperative navigation.
import { useHistory } from 'react-router-dom';
...
const Books = ({ books, onBookSelect }) => {
const history = useHistory(); // <-- access the history object from hook
const sendBookId = (e) => {
const bookId = e.target.value;
onBookSelect(bookId);
history.push("/details"); // <-- issue PUSH to route
};
return (
<div>
<div className="books">
Books
{books.map((book) => (
<div className="book">
<h2>{book.name}</h2>
<p>
Author: {book.author}
</p>
<button className="btn" value={book.id} onClick={sendBookId}>
View Details
</button>
</div>
))}
</div>
</div>
);
};
I have created a blog post. Posts are in the card[Event.js] and on click of the button. It should go to the new page and render its card details there. How can I do it using the react hooks and useParams.?
EventList.js ---> is where I'm fetching the data from api
Event.js ---> I'm rendering the fetched data in cards
EventDetails.js ---> It's the card details that should render on the screen when clicked on the post. Right now I have hard coded. the details.
Could someone please help me with how to do this?
//EventList.js
import React, { useState, useEffect } from "react";
import Event from "../Event/Event";
import axios from "axios";
import styles from "./EventList.module.css";
const EventList = () => {
const [posts, setPosts] = useState("");
let config = { Authorization: "..........................." };
const url = "............................................";
useEffect(() => {
AllPosts();
}, []);
const AllPosts = () => {
axios
.get(`${url}`, { headers: config })
.then((response) => {
const allPosts = response.data.articles;
console.log(response);
setPosts(allPosts);
})
.catch((error) => console.error(`Error: ${error}`));
};
return (
<div>
<Event className={styles.Posts} posts={posts} />
</div>
);
};
export default EventList;
//Event.js
import React from "react";
import styles from "./Event.module.css";
import { Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
const Event = (props) => {
const displayPosts = (props) => {
const { posts } = props;
if (posts.length > 0) {
return posts.map((post) => {
return (
<div>
<div>
<div className={styles.post}>
<img
src={post.urlToImage}
alt="covid"
width="100%"
className={styles.img}
/>
<div>
<h3 className={styles.title}>{post.title}</h3>
<div className={styles.price}> {post.author} </div>
<Link to={`/${post.title}`}>
<button className={styles.btns}> {post.author} </button>
</Link>
</div>
</div>
</div>
</div>
);
});
}
};
return <div className="Posts">{displayPosts(props)}</div>;
};
export default Event;
//EventDetails.js
import React, { useState, useEffect } from "react";
import Navbar from "../Navbar/Navbar";
import DetailsImage from "../../assets/Event-Ticketing.png";
import styles from "./EventDetails.module.css";
import "bootstrap/dist/css/bootstrap.min.css";
import { Link, useParams, useLocation } from "react-router-dom";
import axios from "axios";
// let config = { Authorization: "3055f8f90fa44bbe8bda05385a20690a" };
// const baseurl = "https://newsapi.org/v2/top-headlines?sources=bbc-news";
const EventDetails = (props) => {
const { state } = useLocation();
if (!state) return null;
// const [title, setTitle] = useState("");
// const { eventName } = useParams();
// useEffect(() => {
// axios
// .get(`${baseurl}`, { headers: config })
// .then((response) => setTitle(response.data));
// }, []);
// useEffect(() => {
// const neweventName = baseurl.find(
// (eventNames) => eventNames.eventName === parseInt(eventName)
// );
// setTitle(neweventName.title);
// }, []);
return (
<div>
<Navbar />
<div className={styles.eventBg}>
<div className="container">
<div>
<img
src={DetailsImage}
alt="ticket"
width="100%"
className={styles.heroEventImage}
/>
</div>
<div className={styles.bookingDetails}>
<div className={styles.nameBook}>
<div>
<div className={styles.eventNameHeader}>
<h1 className={styles.eventName}> {props.title}</h1>
</div>
<div className={styles.genre}>
<div className={styles.genreText}>{props.author}</div>
</div>
</div>
<div className={styles.bookingBtn}>
<div className={styles.booking}>
<Link to="/GeneralBooking">
<button
className={styles.bookBtn}
style={{ height: "60px", fontSize: "18px" }}
>
Book
</button>
</Link>
</div>
</div>
</div>
<div className={styles.venueTime}>
<div className={styles.dateTime}>
<div className={styles.dateTimeText}>{props.author}</div>
<div className={styles.price}>{props.author}</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default EventDetails;
//App.js
import "./App.css";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./components/Home/Home";
import EventDetails from "./components/EventDetails/EventDetails";
import GeneralBooking from "./components/GeneralBooking/GeneralBooking";
import AllotedSeated from "./components/AllotedSeated/AllotedSeated";
import Checkout from "./components/Checkout/Checkout";
function App() {
return (
<BrowserRouter>
<div className="App">
<Switch>
<Route path="/" exact>
<Home />
</Route>
<Route path="/:title" exact children={<EventDetails />}></Route>
<Route path="/GeneralBooking" exact>
<GeneralBooking />
</Route>
</Switch>
{/* <EventDetails /> */}
{/* <GeneralBooking /> */}
{/* <AllotedSeated /> */}
{/* <Checkout /> */}
</div>
</BrowserRouter>
);
}
export default App;
Since it doesn't appear as though you've stored the posts state sufficiently high enough in the ReactTree to be accessible by component on other routes I suggest using route state to send a specific post object to a receiving route.
Event - Update the Link to send also the post object.
<Link
to={{
pathname: `/${post.title}`,
state: { post },
}}
>
<button type="button" className={styles.btns}>{post.author}</button>
</Link>
EventDetails - Use the useLocation hook to access the route state.
import { useLocation } from "react-router-dom";
const EventDetails = (props) => {
const { state } = useLocation();
if (!state.post) return null;
return (
// ...render all the post fields available from state.post
// i.e. state.post.title
);
};
Description of problem:
Changing the id (numbers only) of this url via the link tag does not update the page (but does change the url in the adress bar). Hitting refresh afterward will show the updated page.
http://localhost:8080/video/id/7564
Right clicking to open the link in a new tab, or changing the link path to a completely different page works as expected.
My app.js file
import React from 'react'
import { Router, Route, Switch } from 'react-router-dom'
import RenderHomepage from '../components/homePage/RenderHomepage'
import RenderChannelPage from '../components/channelPage/RenderChannelPage'
import RenderVideoPage from '../components/videoPage/RenderVideoPage'
import RenderSearchPage from '../components/searchPage/RenderSearchPage'
import PageNotFound from '../components/PageNotFound'
import history from '../history'
const App = () => {
return (
<div>
<Router history={history}>
<Switch>
<Route path="/" exact component={RenderHomepage} />
<Route path="/channel" component={RenderChannelPage} />
<Route path="/video/id" component={RenderVideoPage} />
<Route path="/search" component={RenderSearchPage} />
<Route path="/404" exact component={PageNotFound} />
<Route component={PageNotFound} />
</Switch>
</Router>
</div>
)
}
export default App
Link tag in UpNextVideos component:
import React from 'react'
import { Link } from 'react-router-dom'
...
<Link to={{pathname: vid.id}}>
<h3 className={`${p}-sidebar-grid-video-title`}>{capitalizeFirstLetter(vid.tags)}</h3>
</Link>
...
How the components in question are nested:
<RenderVideoPage>
<VideoPage>
<UpNextVideos>
RenderVideoPage component:
import React from 'react'
import VideoPage from './VideoPage'
import Header from '../Header'
import HeaderMobile from '../HeaderMobile'
import FooterMobile from '../FooterMobile'
import ActivityFeed from '../ActivityFeed'
const RenderVideoPage = () => {
return (
<div className="videoPage-body">
<HeaderMobile />
<Header />
<ActivityFeed page={'home'} />
<VideoPage />
<FooterMobile page={'video'} />
</div>
)
}
export default RenderVideoPage
VideoPage component:
import React, { useEffect, useState } from 'react'
import axios from 'axios'
import history from '../../history'
import handleMediaQueries from './containers/handleMediaQueries'
import setDislikes from './containers/setDislikes'
import NewSubscribers from './NewSubscribers'
import CommentSection from './CommentSection'
import UpNextVideos from './UpNextVideos'
import DescriptionBox from './DescriptionBox'
import VideoNotFound from './VideoNotFound'
import { fetchVideoFromID, fetchPictureFromID } from '../../containers/api'
import { thumbsUp, thumbsDown } from '../svgs'
import {
abbreviateNumber,
capitalizeFirstLetter,
randomDate } from '../../containers/helperFunctions'
const VideoPage = () => {
const [p, setPrefix] = useState("videoPage")
const [state, setState] = useState({
loading: true,
error: false
})
useEffect(() => {
if (state.loading) extractDataFromUrl()
else handleMediaQueries()
}, [state.loading])
const fetchVideo = async (id, picAuthorID) => {
let response = await fetchVideoFromID(id)
if (!response) setState(prevState => ({...prevState, error: true}))
else mapVideoResponseToHTML(response.data.hits, picAuthorID)
}
const mapVideoResponseToHTML = (response, picAuthorID) => {
let responseAsHtml = response.map(vid => {
return {
video:
<div className={`${p}-video-wrapper posRelative`} key={vid.id}>
<a className={`${p}-pixabay-src`} href={vid.pageURL}>?</a>
<video
poster="https://i.imgur.com/Us5ckqm.jpg"
className={`${p}-video clickable`}
src={vid.videos.large.url}
controls autoPlay>
</video>
<div className={`${p}-video-info-wrapper`}>
<div className={`${p}-video-title-box`}>
<h1 className={`${p}-video-title`}>{capitalizeFirstLetter(vid.tags)}</h1>
<span className={`${p}-video-views`}>{abbreviateNumber(Number(vid.downloads).toLocaleString())} views</span>
<span className={`${p}-video-date`}>{randomDate()}</span>
</div>
<div className={`${p}-video-options`}>
<div className="thumbs">
<div className={`${p}-video-options-thumbsUp`}>{thumbsUp(20)}
<span className={`${p}-video-options-thumbsUp-text`}>{abbreviateNumber(vid.likes)}</span>
</div>
<div className={`${p}-video-options-thumbsDown`}>{thumbsDown(20)}
<span className={`${p}-video-options-thumbsDown-text`}>{setDislikes(vid.likes)}</span>
</div>
<div className={`${p}-video-options-likebar`}></div>
</div>
<span className={`${p}-video-options-share`}>Share</span>
<span className={`${p}-video-options-save`}>Save</span>
<span className={`${p}-video-options-ellipses`}>...</span>
</div>
</div>
</div>,
authorFollowers: vid.views,
vidAuthorID: vid.id,
author: picAuthorID ? 'Loading' : vid.user,
authorAvatar: picAuthorID ? null : vid.userImageURL,
views: vid.downloads
}
})
responseAsHtml = responseAsHtml[0]
setState(prevState => ({...prevState, ...responseAsHtml, loading: false}))
if (picAuthorID) fetchAuthorAvatar(picAuthorID)
}
const extractDataFromUrl = () => {
const currentURL = window.location.href
const urlAsArray = currentURL.split('/')
const urlID = urlAsArray[5].split('-')
const videoID = urlID[0]
const picAuthorID = urlID[1]
// Author avatars are random except on the home page.
// if url isnt from homepage, then use videoID
// if url is from homepage, send that avatarID
if (urlID.includes('000')) {
fetchVideo(videoID)
} else {
setState(prevState => ({...prevState, picAuthorID: picAuthorID}))
fetchVideo(videoID, picAuthorID)
}
}
const fetchAuthorAvatar = async (id) => {
const response = await fetchPictureFromID(id)
const authorName = response.data.hits[0].user
const authorAvatar = response.data.hits[0].previewURL
setState(prevState => ({
...prevState,
authorAvatar: authorAvatar,
author: capitalizeFirstLetter(authorName)
}))
}
return (
<div>
{ state.error ? <VideoNotFound /> : null}
{ state.loading === true ? null
:
<div className={`${p}-page-wrapper`}>
<main className={`${p}-main`}>
{state.video}
<DescriptionBox props={state} />
<div className={`${p}-suggested-videos-mobile`}></div>
<div className={`${p}-new-subscribers-wrapper`}>
<h2 className={`${p}-new-subscribers-text`}>{`New Subscribers to ${state.author}`}</h2>
<NewSubscribers />
</div>
<div className={`${p}-comment-section`}>
<CommentSection views={state.views}/>
</div>
</main>
<aside className={`${p}-sidebar`}>
<UpNextVideos />
</aside>
</div>
}
</div>
)
}
export default VideoPage
UpNextVideos component:
import React, { useEffect, useState, useRef, useCallback } from 'react'
import { Link } from 'react-router-dom'
import axios from 'axios'
import { videoQuery } from '../../words'
import { fetchVideos } from '../../containers/api'
import {
capitalizeFirstLetter,
uuid,
getRandom,
abbreviateNumber
} from '../../containers/helperFunctions'
const UpNextVideos = () => {
const [p, setPrefix] = useState("videoPage")
const [nextVideos, setNextVideos] = useState([])
useEffect(() => {
fetchUpNextVideos(15, getRandom(videoQuery))
}, [])
// INFINITE SCROLL
const observer = useRef()
const lastUpNextVideo = useCallback(lastVideoNode => {
// Re-hookup observer to last post, to include fetch data callback
if (observer.current) observer.current.disconnect()
observer.current = new IntersectionObserver(entries => {
const lastVideo = entries[0]
if (lastVideo.isIntersecting && window.innerWidth <= 1000) {
document.querySelector('.videoPage-show-more-button').classList.add('show')
}
else if (lastVideo.isIntersecting && window.innerWidth > 1000) {
document.querySelector('.videoPage-show-more-button').classList.remove('show')
fetchUpNextVideos(20, getRandom(videoQuery))
}
})
if (lastVideoNode) observer.current.observe(lastVideoNode)
})
const fetchUpNextVideos = async (amount, query) => {
let response = await fetchVideos(amount, ...Array(2), query)
response = response.data.hits
const responseAsHtml = response.map((vid, index) => {
return (
<div className={`${p}-sidebar-grid-video-wrapper`} key={uuid()} ref={response.length === index + 1 ? lastUpNextVideo : null}>
<div className={`${p}-sidebar-grid-video`}>
<a href={`/video/id/${vid.id}-000`}>
<video
className={`${p}-upnext-video`}
onMouseOver={event => event.target.play()}
onMouseOut={event => event.target.pause()}
src={`${vid.videos.tiny.url}#t=1`}
muted >
</video>
</a>
</div>
<a href={`/video/id/${vid.id}`}>
<h3 className={`${p}-sidebar-grid-video-title`}>{capitalizeFirstLetter(vid.tags)}</h3>
</a>
<a href={`/channel/000${vid.id}`}>
<p className={`${p}-sidebar-grid-video-author`}>{vid.user}</p>
</a>
<p className={`${p}-sidebar-grid-video-views-text`}>{abbreviateNumber(vid.downloads)} views</p>
</div>
)
})
setNextVideos(prevState => ([...prevState, ...responseAsHtml]))
}
return (
<div>
<div className={`${p}-sidebar-text-top`}>
<span className={`${p}-sidebar-text-upnext`}>Up next</span>
<span className={`${p}-sidebar-text-autoplay`}>Autoplay</span>
</div>
<div className={`${p}-sidebar-grid-wrapper`}>
{nextVideos}
</div>
<button
className={`${p}-show-more-button`}
onMouseDown={() => fetchUpNextVideos(15, getRandom(videoQuery))}>
Show More
</button>
</div>
)
}
export default UpNextVideos
What I've tried:
Wrapping the <Link> tag with <Router history={history} />
Wrapping the <Link> tag with <BrowserRouter>
Wrapping the export statement withRouter(UpNextVideos)
Using a plain string instead of an object, as described in react-router-docs
Ok, I believe this issue lies in your VideoPage component.
useEffect(() => {
if (state.loading) extractDataFromUrl()
else handleMediaQueries()
}, [state.loading]);
You only ever have state.loading true once, when the component mounts. This only processes your URL once, so when the URL changes this component isn't aware of it.
This is your route currently
<Route path="/video/id" component={RenderVideoPage} />
now assuming your URLs are shaped "/video/id/" then you can define your route to have a parameter
<Route path="/video/id/:videoId" component={RenderVideoPage} />
If you wrap this component with react-router-dom's withRouter HOC you can easily get the id path param and add it to an effect to recompute all the video data.
export default withRouter(VideoPage)
withRouter injects the location, match, and history props from the closest Route ancestor. Here's an example of getting the id param and triggering an effect when its value updates.
const VideoPage = ({ match }) => {
const { params } = match;
useEffect(() => { /* do something with new id */ }, [params.videoId]);
}
This is My App.js where all the Routes define Under Router. It's work fine when i jump from one Link to other in those component that are not using redux. but when i click on Redux connected component it's render component but then when i click on any other Link they just change Url Not view.
This is App js File:-
import React, { useEffect, Fragment } from "react";
import { Router, Route, Switch } from "react-router-dom";
import history from "./history";
import Navbar from "./components/layouts/Navbar";
import Landing from "./components/layouts/Landing";
import Profiles from "./components/profiles/Profiles";
import Login from "./components/auth/Login";
import Register from "./components/auth/Register";
import { loadUser } from "./actions/auth";
import { useDispatch } from "react-redux";
const App = () => {
const dispatch = useDispatch(() => loadUser());
useEffect(() => {
dispatch(loadUser());
}, [dispatch]);
return (
<Router history={history}>
<Navbar />
<Route exact path='/' component={Landing} />
<section className='container'>
<Alert />
<Switch>
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
<Route path='/profiles' component={Profiles} />
</Switch>
</section>
</Router>
);
};
export default App;
Both Register And LogIn Workimg well when navigating through each other but when I jump to component that using redux profiles, it loads and work but after that when i want to jump from profiles to Register login they just change url not view.
this is My profiles file that using redux and creating issue.
import React, { Fragment, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getProfiles } from "../../actions/profile";
import Spinner from "../layouts/Spinner";
import ProfileItems from "./ProfileItems";
import { withRouter } from "react-router-dom";
const Profiles = () => {
const profile = useSelector(state => state.profile);
const { profiles, loading } = profile;
const dispatch = useDispatch(() => getProfiles());
useEffect(() => dispatch(getProfiles()), [dispatch]);
return (
<Fragment>
{loading ? (
<Spinner />
) : (
<Fragment>
<h1 className='large text-primary'>Developers</h1>
<p className='lead'>
<i className='fab fa-connectdevelop'></i> Browse and Connect With
Developers...
</p>
<div className='profiles'>
{profiles.length > 0 ? (
profiles.map(profile => (
<ProfileItems key={profile._id} profile={profile} />
))
) : (
<h4>profile not Found !!!...</h4>
)}
</div>
</Fragment>
)}
</Fragment>
);
};
export default withRouter(Profiles);
And These are My Login And Register component that are working well when navigate to each other. when they go to profiles after that when i click on link of them they just change urls in address bar but not changing view. It's Login page Register is similar to this..
import React, { Fragment, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { login } from "../../actions/auth";
import { Link, Redirect } from "react-router-dom";
const Login = () => {
const dispatch = useDispatch(() => login());
const isAuthenticated = useSelector(state
=>state.auth.isAuthenticated);
const [formData, setFormData] = useState({
email: "",
password: ""
});
const { email, password } = formData;
const onChange = e => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
dispatch(login(email, password));
};
if (isAuthenticated) {
return <Redirect to='/dashboard' />;
}
return (
<Fragment>
<h1 className='large text-primary'>Sign In</h1>
<p className='lead'>
<i className='fas fa-user'>Sign In Your Account!!!</i>
</p>
<form onSubmit={e => onSubmit(e)} className='form'>
<div className='form-group'>
<input
type='email'
name='email'
placeholder='Enter Your Email'
value={email}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input
type='password'
name='password'
placeholder='Enter Your Password'
value={password}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input type='submit' value='LogIn' className='btn btn-primary' />
</div>
</form>
<p className='my-1'>
Don't have an Account <Link to='/register'>Sign Up</Link>
</p>
</Fragment>
);
};
export default Login;
I searched this alot and mostly got ans use withRouter I tried that one as u can see but still not working or maybe i am not using withRouter on correct component.
I'll do Provide any other information that you need to know about my code if you want and I am using react-redux hooks instead of using connect
I had faced the same problem in the past.
At that time I solve this issue with connected-react-router.
this is how to use the connected-react-router.
how to use connected-react-router