I am using react 18 ,redux and react-router dom to navigate to and from pages. HOwever after successful login the screen first shows the homepage for a split second then a loader and then the screen completely goes white. But after only pressing reload manually once the Home component is rendered again. Also my Home page has a lot of nested components in its tree passing props to each other's child.
I wonder if it has something to do with the redux action dispatches?
How to make the Home page load in the first go itself?
Here is my App.js=>
import { Fragment, useEffect, useState } from 'react';
import {BrowserRouter as Router , Routes , Route , Navigate} from 'react-router-dom';
import './App.css';
import Header from './components/Header/Header';
import Home from './components/Home/Home.js';
import LandingPage from './components/Landing/Landing.js'
import Login from './components/Login/Login';
import { useDispatch, useSelector } from 'react-redux';
import { loadUser } from './Actions/UserActions';
import Account from './components/Account/Account.js'
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadUser());
}, [dispatch])
const {isAuthenticated} = useSelector(state=>state.user);
const [reload, setReload] = useState(false);
return (
<Fragment>
<Router>
{isAuthenticated && <Header/>}
<Routes>
<Route path='/' element={!isAuthenticated? (<Navigate to='/login' replace={true} />): (<Home/>)}/>
<Route path='/welcome' element={<LandingPage/>}/>
<Route path='/login' element={!isAuthenticated? <Login/> :(<Navigate to='/' replace={true}/>)}/>
<Route path='/account' element={!isAuthenticated? (<Navigate to='/' />): <Account/>}/>
</Routes>
</Router>
</Fragment>
);
}
export default App;
Here is my Home component=>
import React, { useEffect, useState } from 'react'
import './Home.css'
import User from '../User/User.js'
import Post from '../Post/Post.js'
import { useDispatch, useSelector } from 'react-redux'
import { getAllUsers, getPosts } from '../../Actions/UserActions'
import Loader from '../Loader/Loader'
import { Typography } from '#mui/material'
import Metadata from '../Metadata/Metadata'
const Home = () => {
const dispatch=useDispatch();
const { loading,posts,error} = useSelector(state =>state.getPosts)
const { usersLoading , users ,userError} =useSelector(state=>state.allUsers)
const { error: likeError, message } = useSelector((state) => state.like);
useEffect(() => {
dispatch(getPosts());
dispatch(getAllUsers());
}, [dispatch])
useEffect(() => {
if (error) {
dispatch({ type: "clearErrors" });
}
if (likeError) {
dispatch({ type: "clearErrors" });
}
}, [alert, error, message, likeError, dispatch]);
return loading===true||usersLoading===true? (
<Loader />)
:(
<div className="home">
<Metadata title='Civilised' />
<div className="homeleft">
{
posts&&posts.length>0 ? posts.map((post)=>
(<Post
key={post._id}
postId={post._id}
caption={post.caption}
postImage={post.image.url}
likes={post.likes}
isLiked={false}
comments={post.comments}
ownerImage={post.owner.avatar.url}
ownerName={post.owner.name}
ownerId={post.owner._id}
/>)
): (<Typography variant='h6'>No posts</Typography>)
}
</div>
<div className="homeright">
{
users ? users.map((user)=>(
<User
key={user._id}
userId={user._id}
name={user.name}
avatar={'https://media-exp1.licdn.com/dms/image/C5603AQHlHovW8nMkFA/profile-displayphoto-shrink_200_200/0/1610020467592?e=2147483647&v=beta&t=vBPLqLRHM1Py_hRw7vSbT86TKE7UREGqCFyvoYGyJoc'}
//avatar={currentUser.avatar.url}
/>
)) : null
}
</div>
</div> )
}
export default Home
Here are the warnings I face just after a successful login when the page goes blank=>
Here are the warning I get in the console after I reload the page and the is successfully rendered and functioning well.=>
Related
Below is the image of my console :
cannot read properties
The following is my productDetails page code:
import React,{useEffect}from 'react'
import Carousel from "react-material-ui-carousel"
import "./ProductDetails.css"
import {useSelector,useDispatch} from "react-redux"
import { getProductDetails } from '../../actions/productAction'
const ProductDetails = ({match}) => {
const dispatch=useDispatch();
const{product,loading,error}=useSelector((state)=>state.productDetails)
useEffect(() => {
dispatch(getProductDetails(match.params.id))
}, [dispatch,match.params.id])
return (
<>
<div className="ProductDetails">
<div>
<Carousel>
{ product.images &&
product?.images?.map((item, i) => (
<img
className="CarouselImage"
key={item.url}
src={item.url}
alt={`${i} Slide`}
/>
))}
</Carousel>
</div>
</div>
</>
)
}
export default ProductDetails
**
The following is my App.js Code**
import './App.css';
import Header from './component/layout/Header/Header';
import {BrowserRouter as Router,Route,Routes} from "react-router-dom"
import WebFont from "webfontloader"
import React from "react"
import Footer from './component/layout/Footer/Footer'
import Home from './component/Home/Home';
import Loader from './component/layout/Loader/Loader';
import ProductDetails from './component/Product/ProductDetails';
function App() {
React.useEffect(()=>{
WebFont.load({
google:{
families:["Roboto","Droid Sans","Chilanka"]
}
})
},[])
return (
<Router>
<Header/>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/product/:id' element={<ProductDetails/>}/>
</Routes>
<Footer/>
</Router>
);
}
export default App;
The following is my the getProductDetails function which is used in productDetails page:
export const getProductDetails=(id)=>async(dispatch)=>{
try{
dispatch({type:PRODUCT_DETAILS_REQUEST})
const {data}=await axios.get(`/api/v1/product/${id}`);
dispatch({
type:PRODUCT_DETAILS_SUCCESS,
payload:data.product,
})
}
catch(error){
dispatch({
type:PRODUCT_DETAILS_FAIL,
payload:error.response.data.message,
})
}
}
export const clearErrors=()=>async(dispatch)=>{
dispatch({type:CLEAR_ERRORS})
}
I was expecting that when i click the product its productdetails page should open but it does not open instead it shows blank page :
I think it is due to match.params.id problem. Can anyone suggest me some solution to fix it?
In productDetails component, you can use useParams
instead of using (match.params.id) use only (id)
import { useParams } from 'react-router-dom';
const {id} = useParams();
getProductDetails(id)
Im using use context for storing user data when i loggin. I have the following context component, LoginContext.js:
import React, { createContext, useState } from "react";
import jwt_decode from 'jwt-decode';
const LoginContext = createContext();
export function LoginProvider({children}) {
const [user, setUser] = useState({})
const [isLoggedIn, setIsLoggedIn ] = useState(false)
function handleCallbackResponse(response){
var userData = jwt_decode(response.credential); //Token with the login user Data
setUser(userData); //Store user Data
setIsLoggedIn(true)
/* console.log(userData) */
document.getElementById('signInDiv').hidden = true;
}
function handleSignOut(event) {
setUser({}) //Empy the user Data
document.getElementById('signInDiv').hidden = false;
}
return(
<LoginContext.Provider value={{user, handleCallbackResponse, handleSignOut}}>{children}</LoginContext.Provider>
);
}
export default LoginContext
In my Login.js im using the context above with it functions and properties.
/*global google*/
import React, { useContext, useEffect } from 'react'
import LoginContext from '../LoginContext';
const Login = () => {
const {user, handleCallbackResponse, handleSignOut} = useContext(LoginContext);
useEffect(()=>{
google.accounts.id.initialize({
client_id:"MY client ID",
callback: handleCallbackResponse
})
google.accounts.id.prompt();
window.google.accounts.id.renderButton(
document.getElementById('signInDiv'),
{theme: 'outline', size: 'medium'}
)
}, []);
return (
<div>
<div id="signInDiv"></div>
{
//If user objetc is not empty show sign out button
Object.keys(user).length !== 0 &&
<button onClick={(e)=>handleSignOut(e)}>Sign Out</button>
}
{user &&
<div>
<img src={user.picture} alt="" />
<h3>{user.name}</h3>
</div>
}
</div>
)
}
export default Login
As you can see im accessing to user prop or functions such as handleCallbackResponse that come from my LoginContext.js
But now in my Dashboard.js im trying to use the user prop from LoginContex.js but is not rendering anything.
import React, { useContext } from 'react'
import Navbar from '../organisms/Navbar'
import './Dashboard.css'
import LoginContext from '../LoginContext';
const Dashboard = () => {
const {user} = useContext(LoginContext)
console.log(user)
return (
<div className='dashboard'>
<h2>{user.name}</h2>
<Navbar/>
<div className='content'>
<h1>Welcome to my Dashboard</h1>
</div>
</div>
)
}
export default Dashboard
In my h2 tag im trying to render the user.name but it doesn´t renders anything.
App.js:
import './App.css';
import { useContext } from 'react';
import Login from './atoms/Login';
import { BrowserRouter , Routes, Route } from 'react-router-dom';
import Dashboard from './pages/Dashboard';
import { LoginProvider } from './LoginContext';
function App() {
return (
<LoginProvider >
<BrowserRouter>
<Routes>
<Route exact path="/dashboard" element={ /* isLoggedIn ? */<Dashboard/> /* : <Login /> */}/>
<Route path="/" element={<Login /* setIsLoggedIn={setIsLoggedIn} *//>} />
</Routes>
</BrowserRouter>
</LoginProvider>
);
}
export default App;
I did a console.log of user.name and i get undefined.
I'm trying to integrate Firebase to React using React Context. The React project uses the Able template. When I wrap my App component with ContextProvider, it causes an infinite loop.
Here is the code:
./Firebase/firebase.js
import React, { createContext } from "react";
import { useDispatch } from "react-redux";
import firebaseConfig from "./firebaseConfig";
import app from "firebase/app";
import 'firebase/auth';
import 'firebase/firestore';
import "firebase/database";
import { setLoggedUser } from '../store/actions'
// we create a React Context, for this to be accessible
// from a component later
const FirebaseContext = createContext(null);
export { FirebaseContext };
export default ({ children }) => {
let firebase = {
app: null,
database: null,
};
const dispatch = useDispatch();
// check if firebase app has been initialized previously
// if not, initialize with the config we saved earlier
if (!app.apps.length) {
app.initializeApp(firebaseConfig);
firebase = {
app: app,
database: app.database(),
api: {
getUserProfile,
},
};
}
// function to query logged user from the database and
// fire a Redux action to update the items in real-time
function getUserProfile() {
....
}
};
return <FirebaseContext.Provider value={firebase}>{children}</FirebaseContext.Provider>;
};
./index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom";
import App from "./App/index";
import * as serviceWorker from "./serviceWorker";
import reducer from "./store/reducer";
import config from "./config";
import "./assets/scss/style.scss";
import FirebaseProvider from './Firebase/firebase.js';
const store = createStore(reducer);
const app = (
<Provider store={store}>
<BrowserRouter basename={config.basename}>
<FirebaseProvider> <----- cause infinite loading
<App />
</FirebaseProvider>
</BrowserRouter>
</Provider>
);
ReactDOM.render(app, document.getElementById("root"));
The source code that causes the error is where the loadable import component AdminLayout in App/index.js
./App/index.js
import React, { Component, Suspense } from "react";
import { Switch, Route } from "react-router-dom";
import Loadable from "react-loadable";
import "../../node_modules/font-awesome/scss/font-awesome.scss";
import Loader from "./layout/Loader";
import Aux from "../hoc/_Aux";
import ScrollToTop from "./layout/ScrollToTop";
import routes from "../route";
import { FirebaseContext } from '../Firebase/firebase.js';
const AdminLayout = Loadable({
loader: () => {
debugger
return import("./layout/AdminLayout")}, // Cause Infinite Loading
loading: Loader,
});
const App = () => {
const { app, api } = React.useContext(FirebaseContext);
const menu = routes.map((route, index) => {
return route.component ? (
<Route
key={index}
path={route.path}
exact={route.exact}
name={route.name}
render={(props) => <route.component {...props} />}
/>
) : null;
});
return (
<Aux>
<ScrollToTop>
<Suspense fallback={<Loader />}>
<Switch>
{menu}
<Route path="/" component={AdminLayout} />
</Switch>
</Suspense>
</ScrollToTop>
</Aux>
);
}
export default App;
I lost in the debugging process when I try to know what's going on inside this AdminLayout component. This component is coming from the template.
./App/layout/AdminLayout/index.js
import React, { Component, Suspense } from "react";
import { Route, Switch, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import Fullscreen from "react-full-screen";
import windowSize from "react-window-size";
import Navigation from "./Navigation";
import NavBar from "./NavBar";
import Breadcrumb from "./Breadcrumb";
import Configuration from "./Configuration";
import Loader from "../Loader";
import routes from "../../../routes";
import Aux from "../../../hoc/_Aux";
import * as actionTypes from "../../../store/actions";
//import '../../../app.scss';
class AdminLayout extends Component {
fullScreenExitHandler = () => {
if (
!document.fullscreenElement &&
!document.webkitIsFullScreen &&
!document.mozFullScreen &&
!document.msFullscreenElement
) {
this.props.onFullScreenExit();
}
};
UNSAFE_componentWillMount() {
if (
this.props.windowWidth > 992 &&
this.props.windowWidth <= 1024 &&
this.props.layout !== "horizontal"
) {
this.props.onUNSAFE_componentWillMount();
}
}
mobileOutClickHandler() {
if (this.props.windowWidth < 992 && this.props.collapseMenu) {
this.props.onUNSAFE_componentWillMount();
}
}
render() {
/* full screen exit call */
document.addEventListener("fullscreenchange", this.fullScreenExitHandler);
document.addEventListener(
"webkitfullscreenchange",
this.fullScreenExitHandler
);
document.addEventListener(
"mozfullscreenchange",
this.fullScreenExitHandler
);
document.addEventListener("MSFullscreenChange", this.fullScreenExitHandler);
const menu = routes.map((route, index) => {
return route.component ? (
<Route
key={index}
path={route.path}
exact={route.exact}
name={route.name}
render={(props) => <route.component {...props} />}
/>
) : null;
});
let mainClass = ["pcoded-wrapper"];
if (
this.props.layout === "horizontal" &&
this.props.subLayout === "horizontal-2"
) {
mainClass = [...mainClass, "container"];
}
return (
<Aux>
<Fullscreen enabled={this.props.isFullScreen}>
<Navigation />
<NavBar />
<div
className="pcoded-main-container"
onClick={() => this.mobileOutClickHandler}
>
<div className={mainClass.join(" ")}>
<div className="pcoded-content">
<div className="pcoded-inner-content">
<Breadcrumb />
<div className="main-body">
<div className="page-wrapper">
<Suspense fallback={<Loader />}>
<Switch>
{menu}
<Redirect from="/" to={this.props.defaultPath} />
</Switch>
</Suspense>
</div>
</div>
</div>
</div>
</div>
</div>
<Configuration />
</Fullscreen>
</Aux>
);
}
}
const mapStateToProps = (state) => {
debugger
return {
defaultPath: state.defaultPath,
isFullScreen: state.isFullScreen,
collapseMenu: state.collapseMenu,
layout: state.layout,
subLayout: state.subLayout,
};
};
const mapDispatchToProps = (dispatch) => {
debugger
return {
onFullScreenExit: () => dispatch({ type: actionTypes.FULL_SCREEN_EXIT }),
onUNSAFE_componentWillMount: () =>
dispatch({ type: actionTypes.COLLAPSE_MENU }),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(windowSize(AdminLayout));
Could anyone have an idea why this is happening or maybe how to debug to find the problem? Thank you.
EDIT: When I import the AdminLayout directly without using Loadable, it works fine. How to make this work using Loadable?
I have a component called OneUser , i have input field and button , on clicking that button i will fetch the new title from API and route to new page ,title will be shown in that component called Display.
What is happening now is , on clicking of submit button , routing is happening , but API call is not working .
Please suggest the appropriate answer for me
State logic is working fine , so i didnt posted here.
App.js
import React from 'react';
import './App.css';
import OneUser from './Components/OneUser';
import Display from './Components/Display';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import store from './store'
import {Provider} from 'react-redux'
function App() {
const isAuthenticated = true
return (
<Provider store={store}>
<Router>
<div className="App">
<Switch>
<Route path='/' exact component={OneUser}/>
<Route path='/post' exact component={Display}/>
</Switch>
</div>
</Router>
</Provider>
);
}
export default App;
OneUser.js
import React , {useState, useEffect} from 'react'
import {oneFetchData} from '../redux/oneAction'
import {connect} from 'react-redux'
function OneUser({user,oneFetchData, history}) {
const [id,setId] = useState(1)
const [IdFromButton,setIdFromButton] = useState(1)
const submit = () => {
setIdFromButton(id);
history.push("/post")
}
useEffect( () =>{
oneFetchData(id)
},[IdFromButton]
)
return(
<div>
<input type="text" value={id} onChange={e=> setId(e.target.value)}>
</input>
< button onClick={submit}>Fetch Post</button>
</div>
)
}
const gettingValue = state => {
return{
user: state.data
}
}
const callingAction = dispatch => {
return{
oneFetchData: (id)=>
dispatch(oneFetchData(id))
console.log('dispatched') // not dispatching here function
}
}
Display.js
import React from 'react'
import {useSelector} from 'react-redux'
function Display({history}) {
const user = useSelector( state => state.data)
return (
<div>
<h1>
Title from another component - {user.title}
</h1>
</div>
)
}
export default Display
I think your component may be unmounted before the state changes. To fix that, you should call history.push after IdFromButton has changed.
const [id,setId] = useState(1)
const [IdFromButton,setIdFromButton] = useState(1)
const submit = () => {
setIdFromButton(id);
}
useEffect( () =>{
oneFetchData(id)
history.push("/post")
},[IdFromButton]
)
Hi I have a scenario where I put a search bar on the top nav so a user can search from anywhere in the app. How to do I switch to the results component once the user submits the search form? Here's my search component that populates the global state with search results but I can't manage to switch the view to the results component.
import React, { useState, useEffect, useContext } from 'react';
import axios from 'axios';
import { StateContext } from '../../StateContext';
import './SearchBar.scss';
import sprite from '../../assets/icons/sprite.svg';
function SearchBar() {
const [state, setState] = useContext(StateContext);
const [userInput, setUserInput] = useState('');
const [bookName, setBookName] = useState('');
useEffect(() => {
axios
.get(`https://www.googleapis.com/books/v1/volumes?q=${bookName}`)
.then((res) => {
let book_list = res.data.items;
setState({
book_list: book_list,
heading: 'Search Results'
});
})
.catch((err) => console.log(err));
}, [bookName]);
const findBook = (e) => {
e.preventDefault();
setBookName(userInput);
};
const onChange = (e) => {
setUserInput(e.target.value);
};
return (
<form className='searchbar' onSubmit={findBook}>
<input
type='search'
className='searchbar__input'
placeholder='Search for a book'
value={userInput}
onChange={onChange}
/>
<button className='searchbar__button'>
<svg className='searchbar__icon'>
<use xlinkHref={`${sprite}#icon-search`} />
</svg>
</button>
</form>
);
}
export default SearchBar;
Here's how I'm handling routing:
import React from 'react';
import Nav from './components/Nav/Nav';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Books from './containers/Books';
import Book from './containers/Book';
import { ContextController } from './StateContext';
function App() {
return (
<ContextController>
<Router>
<div className='app'>
<Nav />
<main>
<Switch>
<Route exact path='/' component={Books} />
<Route exact path='/book/:id' component={Book} />
</Switch>
</main>
</div>
</Router>
</ContextController>
);
}
export default App;
If you have a dedicated route for search results, try this in your ContextController
import { useHistory } from 'react-router-dom';
// later
const history = useHistory();
React.useEffect(() => {
if (state?.book_list?.length > 0) {
history.push('/search-results');
}
}, [state]);
Also, it is important to note that the Router should be on top of your Data Context;
Because if you want to access the history from the a tree, it needs to be wrapped in a Router, or else it will return undefined as a value for history
Here is a working codesandbox