React Router Not Working As I would Expect - javascript

I am fairly new to React and am having trouble having a component load when my URL changes.
As a little background, I am using Fireact (https://www.fireact.dev/) as a starting point for this project. What I am trying to do is dynamically load a page with two parameters (accountId and analyticsID). The URL sucesffully changes from /account/QQAH7q5Iz1gwOvpJ4WlA/Analytics to http://localhost:3000/account/QQAH7q5Iz1gwOvpJ4WlA/Analytics/NebYvepZw8IFWmWugPHBbut the target component doesn't load (AnalyticsFeatureView). The target component (AnalyticsFeatureView) does load if I debug this to the root route so it doesn't appear to be an error with the target component. Could this have to do with having two parameters within the App.Js file loading <AuthRouter path="/account/:accountId/:path?" component={Feature} template={AccountTemplate} title="Feature" role="*" /> or something like that?
Please let me know any other information you need.
I currently have the following code snippets between what I believe to be the four relevant files:
App.js
import React from 'react';
import { BrowserRouter as Router, Switch } from "react-router-dom";
import { AuthProvider } from './components/FirebaseAuth';
import PublicRouter from './components/routers/PublicRouter';
import PublicTemplate from './components/templates/PublicTemplate';
import AccountTemplate from './components/templates/AccountTemplate';
import AuthRouter from './components/routers/AuthRouter';
import SignIn from './pages/public/SignIn';
import Home from './pages/auth/Home';
import PageNotFound from './pages/public/PageNotFound';
import AppTemplate from './components/templates/AppTemplate';
import UserProfile from './pages/auth/user/UserProfile';
import UpdateEmail from './pages/auth/user/UpdateEmail';
import UpdateName from './pages/auth/user/UpdateName';
import VerifyEmail from './pages/auth/user/VerifyEmail';
import UpdatePassword from './pages/auth/user/UpdatePassword';
import UpdatePhone from './pages/auth/user/UpdatePhone';
import DeleteUser from './pages/auth/user/DeleteUser';
import ViewLogs from './pages/auth/user/ViewLogs';
import Plans from './pages/auth/accounts/Plans';
import NewAccount from './pages/auth/accounts/NewAccount';
// load stripe
import stripeJson from "./inc/stripe.json";
import { Elements } from '#stripe/react-stripe-js';
import { loadStripe } from '#stripe/stripe-js';
import { Feature } from './pages/auth/accounts/Feature';
import UserList from './pages/auth/accounts/UserList';
import UserRole from './pages/auth/accounts/UserRole';
import AddUser from './pages/auth/accounts/AddUser';
import Invite from './pages/auth/user/Invite';
import PaymentList from './pages/auth/accounts/PaymentList';
import PaymentMethod from './pages/auth/accounts/PaymentMethod';
import DeleteAccount from './pages/auth/accounts/DeleteAccount';
import AnalyticsView from './features/fireact-demo-feature-main/AnalyticsFeatureView';
const stripePromise = loadStripe(stripeJson.stripeConfig.public_api_key);
function App() {
return (
<Elements stripe={stripePromise}>
<AuthProvider>
<Router>
<Switch>
<AuthRouter exact path="/" component={Home} template={AppTemplate} title="My Accounts" />
<AuthRouter exact path="/account/:accountId/billing/plan" component={Plans} template={AccountTemplate} title="Select Plan" role="admin" allowInactive={true} />
<AuthRouter exact path="/account/:accountId/billing/payment-method" component={PaymentMethod} template={AccountTemplate} title="Update Payment Method" role="admin" />
<AuthRouter exact path="/account/:accountId/billing/delete" component={DeleteAccount} template={AccountTemplate} title="Delete Account" role="admin" />
<AuthRouter exact path="/account/:accountId/users/change/:userId" component={UserRole} template={AccountTemplate} title="Change Role" role="admin" />
<AuthRouter exact path="/account/:accountId/users" component={UserList} template={AccountTemplate} title="Users" role="admin" />
<AuthRouter exact path="/account/:accountId/users/add" component={AddUser} template={AccountTemplate} title="Add User" role="admin" />
<AuthRouter exact path="/account/:accountId/billing" component={PaymentList} template={AccountTemplate} title="Billing" role="admin" />
<AuthRouter eact path="/account/:accountId/Analytics/:analyticsId" component={Feature} template={AccountTemplate} title="Feature" role="*" />
<AuthRouter path="/account/:accountId/:path?" component={Feature} template={AccountTemplate} title="Feature" role="*" />
<AuthRouter exact path="/new-account" component={NewAccount} template={AppTemplate} title="Create New Account" />
<AuthRouter exact path="/user/profile" component={UserProfile} template={AppTemplate} title="User Profile" />
<AuthRouter exact path="/invite/:code" component={Invite} template={AppTemplate} title="View Invite" />
<AuthRouter exact path="/user/profile/update-email" component={UpdateEmail} template={AppTemplate} title="Change Your Email" />
<AuthRouter exact path="/user/profile/update-name" component={UpdateName} template={AppTemplate} title="Change Your Name" />
<AuthRouter exact path="/user/profile/verify-email" component={VerifyEmail} template={AppTemplate} title="Verify Your Name" />
<AuthRouter exact path="/user/profile/update-password" component={UpdatePassword} template={AppTemplate} title="Change Your Password" />
<AuthRouter exact path="/user/profile/update-phone" component={UpdatePhone} template={AppTemplate} title="Change Your Phone Number" />
<AuthRouter exact path="/user/profile/delete" component={DeleteUser} template={AppTemplate} title="Delete Your Account" />
<AuthRouter exact path="/user/log" component={ViewLogs} template={AppTemplate} title="View Activity Logs" />
<PublicRouter exact path="/signin" component={SignIn} template={PublicTemplate} title="Sign In" />
<PublicRouter component={PageNotFound} template={PublicTemplate} title="Page Not Found" />
</Switch>
</Router>
</AuthProvider>
</Elements>
);
}
export default App;
FeatureRoutes.js
import React from 'react';
import { Route, Switch, useParams } from "react-router-dom";
import Home from '.';
import ModelsTable from './ModelsNew';
import TemplatesTable from './TemplatesNEW';
import Analytics from './AnalyticsFeature';
import NewModel from './NewModel';
import AnalyticsView from './AnalyticsFeatureView';
const FeatureRoutes = () => {
const { accountId, analyticsID } = useParams();
return (
<Switch key="demo">
<Route path={'/account/' + accountId + '/Analytics/' + analyticsID} >
<AnalyticsView />
</Route>
<Route path={'/account/' + accountId +'/templates'}>
<TemplatesTable />
</Route>
<Route path={'/account/' + accountId +'/Models'} >
<ModelsTable />
</Route>
<Route path={'/account/' + accountId + '/Model/New'} >
<NewModel />
</Route>
<Route path={'/account/' + accountId + '/Analytics'} >
<Analytics />
</Route>
<Route path={'/account/' + accountId + '/testing'} role="admin">
<TemplatesTable />
</Route>
<Route>
<Home />
</Route>
</Switch>
)
}
export default FeatureRoutes;
AnalyticsFeature.js
import React, { useState, useContext, useEffect, useRef } from "react";
import { BreadcrumbContext } from '../../components/Breadcrumb';
import { FirebaseAuth } from "../../components/FirebaseAuth/firebase";
import { useHistory, Redirect, useParams } from 'react-router-dom';
import Loader from '../../components/Loader';
import DataTable from "../../components/DataTable";
import { Card, Button, CardActions, Grid, CardHeader } from "#mui/material";
import { AuthContext } from "../../components/FirebaseAuth";
import { CloudFunctions } from "../../components/FirebaseAuth/firebase";
const Analytics = () => {
const title = 'Analytics';
const history = useHistory();
const { userData } = useContext(AuthContext);
const { setBreadcrumb } = useContext(BreadcrumbContext);
const [loading, setLoading] = useState(true);
const [accounts, setAccounts] = useState([]);
const mountedRef = useRef(true);
const showAnalytics = () => {
setLoading(true);
let records = [];
//const accountsRef = FirebaseAuth.firestore().collection('accounts');
//let query = accountsRef.where('access', 'array-contains', FirebaseAuth.auth().currentUser.uid);
const getAnalytics = CloudFunctions.httpsCallable('getAnalytics');
getAnalytics({
accountId: userData.currentAccount.id
}).then(accountSnapshots => {
if (!mountedRef.current) return null
accountSnapshots.data.forEach(account => {
records.push({
'id': account.id,
'analyticsName': account.name,
'analyticsDescription': account.description,
'analyticsURL': account.url
});
});
setAccounts(records);
setLoading(false);
});
}
useEffect(() => {
setBreadcrumb([
{
to: "/",
text: "Home",
active: false
},
{
to: "/account/" + userData.currentAccount.id + "/",
text: userData.currentAccount.name,
active: false
},
{
to: null,
text: title,
active: true
}
]);
showAnalytics();
}, [userData, setBreadcrumb, history]);
useEffect(() => {
return () => {
//mountedRef.current = false
}
}, []);
//~~~~~~~~~THE BELOW NEEDS LOADING INDICATORS
return (
<>
<div style={{ marginTop: '20px', marginBottom: '20px', textAlign: 'right' }}>
<Button variant="contained" onClick={() => history.push("/account/" + userData.currentAccount.id + "/Analytics/New")}><i className="fa fa-plus"></i>Add Report</Button>
</div >
<Grid item xs={12} container spacing={4}>
{accounts.map((account, i) =>
<Grid container item xs={12} md={12} key={i}>
<Card key={account.id} style={{ width: '25%' }}>
<CardHeader title={account.analyticsName} />
<div padding-left='30px'>
{account.analyticsDescription} <br /><br />
{userData.currentAccount.id}<br /><br />
{account.id }
</div>
<CardActions>
<Button size="small" color="primary" onClick={() => history.push('/account/' + userData.currentAccount.id + '/Analytics/' + account.id)}>View Report</Button>
</CardActions>
</Card>
</Grid>
)}
</Grid>
</>
)
}
export default Analytics;
Feature.js
import { Chip } from "#mui/material";
import React, { useContext, useEffect, Suspense, useState } from "react";
import { BreadcrumbContext } from '../../../../components/Breadcrumb';
import { AuthContext } from "../../../../components/FirebaseAuth";
import Loader from "../../../../components/Loader";
const components = [];
// Here: catch and return another lazy (promise)
const requireComponents = require.context(
'../../../../features', // components folder
true, // look subfolders
/\w+\/FeatureRoutes\.(js)$/ //regex for files
);
requireComponents.keys().forEach((filePath) => {
const folder = filePath.split("/")[1];
const name = filePath.split("/")[2];
const Feature = React.lazy(() =>
import("../../../../features/" + folder + "/" + name));
components.push(<Feature key={components.length+1} />);
});
export const titleContext = React.createContext();
export const Feature = () => {
const [ title, setTitle ] = useState("Default Feature");
const { userData } = useContext(AuthContext);
const { setBreadcrumb } = useContext(BreadcrumbContext);
useEffect(() => {
setBreadcrumb([
{
to: "/",
text: "Home",
active: false
},
{
to: "/account/"+userData.currentAccount.id+"/",
text: userData.currentAccount.name,
active: false
},
{
to: null,
text: title,
active: true
}
]);
}, [userData, setBreadcrumb, title]);
return (
<>
{(components.length > 0)?(
<titleContext.Provider value={{title, setTitle}} >
<Suspense fallback={<Loader />}>
{components}
</Suspense>
</titleContext.Provider>
):(
<>
<div>This is the default feature</div>
</>
)}
</>
)
}
I have tried creating different route patterns within the App.Js and FeatureRouter.js files. I have also successfully loaded the target component within the root directory.

FYI - For the offchance that someone is having this error.
I added a new folder called "analytics" and file titled "FeatureRoutes".
I added the following code to the "FeatureRoutes"
import React from 'react';
import { Route, Switch, useParams } from "react-router-dom";
import AnalyticsView from './AnalyticsFeatureView';
const FeatureRoutes = () => {
const { accountId, analyticsID } = useParams();
return (
<Switch key="demo">
<Route exact path={'/account/:accountId/Analytics/:analyticsID'} >
<AnalyticsView />
</Route>
</Switch>
)
}
export default FeatureRoutes;
Added my "AnalyticsFeatureView" component to the "analytics" folder

Related

React app display blank page without any compilation error

I am not sure what went wrong with my react app below, it compile successfully without error but doesn't show anything (just show a blank page). Can anyone point out what went wrong with my code? Sorry I am new to react.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css'
import { BrowserRouter } from 'react-router-dom'
import App from './App';
ReactDOM.render((
// <Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
// </Provider>
), document.getElementById('root'))
App.js
import { BrowserRouter as Router, Route, Link, NavLink } from "react-router-dom";
import Home from "./components/Home";
import NewBuilding from "./components/NewBuilding";
import React, { Component } from "react";
import Web3 from 'web3';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import "./App.css";
import { useEffect, useState } from 'react';
function App() {
const [currentAccount, setCurrentAccount] = useState(null);
const checkWalletIsConnected = async () => {
const { ethereum } = window;
if (!ethereum) {
console.log("Make sure you have Metamask installed!");
return;
} else {
console.log("Wallet exists! We're ready to go!")
}
const accounts = await ethereum.request({ method: 'eth_accounts' });
if (accounts.length !== 0) {
const account = accounts[0];
console.log("Found an authorized account: ", account);
setCurrentAccount(account);
} else {
console.log("No authorized account found");
}
}
const connectWalletHandler = async () => {
const { ethereum } = window;
if (!ethereum) {
alert("Please install Metamask!");
}
try {
const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
console.log("Found an account! Address: ", accounts[0]);
setCurrentAccount(accounts[0]);
} catch (err) {
console.log(err)
}
}
const connectWalletButton = () => {
return (
<button onClick={connectWalletHandler} className='cta-button connect-wallet-button'>
Connect Wallet
</button>
)
}
useEffect(() => {
checkWalletIsConnected();
}, [])
return (
<div>
<Router>
<AppBar position="static" color="default" style={{ margin: 0 }}>
<Toolbar>
<Typography variant="h6" color="inherit">
<NavLink className="nav-link" to="/">Home</NavLink>
</Typography>
<NavLink className="nav-link" to="/new/">New</NavLink>
</Toolbar>
</AppBar>
<Route path="/" exact component={Home} />
<Route path="/new/" component={NewBuilding} />
</Router>
</div>
)
}
export default App;
My Home.js is residing in a components folder
The folder structure is as below
Home.js
import React, { useState, useEffect } from "react";
import { makeStyles } from '#material-ui/core/styles';
import Web3 from 'web3'
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1),
},
input: {
display: 'none',
},
}));
const Home = () => {
const classes = useStyles();
const [ contract, setContract] = useState(null)
const [ accounts, setAccounts ] = useState(null)
const [ funds, setFunds ] = useState([])
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
useEffect(() => {
}, []);
return (
<div><h2>Home</h2></div>
)
}
export default Home;
Your Route component should have the prop element instead of component, see the migration guide.
<Route path="/" exact element={<Home />} />
In App.js you're wrapping your components again in a Router. Instead you should wrap your Route components in a Routes.
import { Routes, Route, Link, NavLink } from "react-router-dom";
...
<div>
<AppBar position="static" color="default" style={{ margin: 0 }}>
<Toolbar>
<Typography variant="h6" color="inherit">
<NavLink className="nav-link" to="/">
Home
</NavLink>
</Typography>
<NavLink className="nav-link" to="/new/">
New
</NavLink>
</Toolbar>
</AppBar>
<Routes>
<Route path="/" exact element={<Home />} />
<Route path="/new/" element={<NewBuilding />} />
</Routes>
</div>

React Localstorage values resetting after refresh

when i add new items to favorites localStorage, all my items in favorites storage resetting and new added items saving in the localStorage. if i dont add any new item it works fine.
how can i stop the reset? im probably doing something wrong with setFavorite state but i don't know.
App.js
import "./App.css";
import React, { Profiler, useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Login from "./components/Login/Login";
import SignUp from "./components/SignUp/SignUp";
import Home from "./components/Home/Home";
import ChuckNorris from "./components/ListAPI/ChuckNorris/ChuckNorris";
import CreatePost from "./components/CreatePost/CreatePost";
import RandomUser from "./components/ListAPI/RandomUser/RandomUser";
import Pokemon from "./components/ListAPI/Pokemon/Pokemon";
import Valorant from "./components/ListAPI/Valorant/Valorant";
import Recipes from "./components/ListAPI/Recipes/Recipes";
import { MainContext } from "./components/Context";
import { getDocs, collection, deleteDoc, doc } from "firebase/firestore";
import { db, auth } from "./firebase";
import Favorites from "./components/Favorites/Favorites";
function App() {
const [isAuth, setIsAuth] = useState(localStorage.getItem("isAuth"));
const [modu, setModu] = useState(false);
const [postLists, setPostList] = useState([]);
const [favorites, setFavorites] = useState(localStorage.getItem("dam"));
const postsCollectionRef = collection(db, "posts");
useEffect(() => {
const getPosts = async () => {
const data = await getDocs(postsCollectionRef);
setPostList(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
getPosts();
}, []);
useEffect(() => {
const localData = localStorage.getItem("dam") ?? [];
setFavorites(localData);
}, [setFavorites]);
const addFavorite = (favorite) => {
setFavorites([...favorites, favorite]);
localStorage.setItem("dam", JSON.stringify(favorite));
};
const data = {
postLists,
setPostList,
favorites,
setFavorites,
addFavorite,
};
return (
<>
<MainContext.Provider value={data}>
<Router>
<Routes>
<Route
exact
path="/"
element={<Home isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/login"
element={<Login isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/createpost"
element={<CreatePost isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/signup"
element={<SignUp isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/chucknorris"
element={<ChuckNorris isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/pokemon"
element={<Pokemon isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/randomuser"
element={<RandomUser isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/valorant"
element={<Valorant isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/recipes"
element={<Recipes isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/favorites"
element={<Favorites isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
</Routes>
</Router>
</MainContext.Provider>
</>
);
}
export default App;
Favorites.js
import React, { useEffect, useState } from "react";
import Sidebar from "../Sidebar/Sidebar";
import PostList from "../Home/PostList";
import { useContext, MainContext } from "../Context";
import "./Favorites.css";
const Favorites = ({ isAuth, setIsAuth, addFavorite }) => {
const { postLists, setPostList, favorites, setFavorites } =
useContext(MainContext);
return (
<>
{" "}
<div className="containers">
<div className="sidebar">
<Sidebar isAuth={isAuth} setIsAuth={setIsAuth} />
<div className="norris mt-4">
<div className="favorite-container">
<div className="new-container">
{postLists
.filter((post) => favorites.includes(post))
.map((post) => (
<PostList
post={post}
addFavorite={addFavorite}
key={post.id}
/>
))}
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default Favorites;
Postlist.js
import React from "react";
const PostList = ({ post, addFavorite }) => {
const { linkin, title, imageURL, photoURL, name, id } = post;
return (
<>
<div>
<div className="post">
<div className="postimage">
<div className="del"></div>
<div className="images">
<a href={linkin}>
<p className="ss">{title}</p>
<img src={imageURL} id="img-photo" />
</a>
<div className="uploader">
<img src={photoURL} />
<p>by {name}</p>
</div>
{addFavorite && (
<div className="butons">
<button onClick={() => addFavorite(id)} id="favori">
+
</button>
</div>
)}
</div>
</div>
</div>
</div>
</>
);
};
export default PostList;
Your addFavorite function saves favorite to local storage instead of saving it the same way you set your local state [...favorites, favorite]
const addFavorite = (favorite) => {
setFavorites([...favorites, favorite]);
localStorage.setItem("dam", JSON.stringify(favorite)); <- this also needs to be [...favorites, favorite]
};

Pagination in react not working. Adds in extra to the URL string once loaded

hi hope someone can help me. I have added pagination into my react app. I have checked and the query strings are working as they should be on the back end. However when I try and click through the pagination buttons it adds extra to the URL so instead of localhost:3000/topic/page/1 it is localhost:3000/topic/page/topic/page/1. Therefore I get a not found error when I click on the link.
Routes file
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Register from '../auth/Register';
import Login from '../auth/Login';
import Alert from '../layout/Alert';
import Dashboard from '../dashboard/Dashboard';
import CreateProfile from '../profile-forms/CreateProfile';
import EditProfile from '../profile-forms/EditProfile';
import Profiles from '../profiles/Profiles';
import Profile from '../profile/Profile';
import Posts from '../posts/Posts';
import Post from '../post/Post';
import Topics from '../topic/Topics';
import Flashcard from '../flashcard/Flashcard';
import Quiz from '../quiz/Quiz';
import Factsheet from '../factsheet/Factsheet';
import NotFound from '../layout/NotFound';
import PrivateRoute from '../routing/PrivateRoute';
const Routes = () => {
return (
<section className="container">
<Alert />
<Switch>
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<PrivateRoute exact path="/profiles" component={Profiles} />
<PrivateRoute exact path="/profile/:id" component={Profile} />
<PrivateRoute exact path="/dashboard" component={Dashboard} />
<PrivateRoute exact path='/create-profile' component={CreateProfile} />
<PrivateRoute exact path='/edit-profile' component={EditProfile} />
<PrivateRoute exact path='/topic' component={Topics} />
<PrivateRoute exact path='/topic/search/:keyword' component={Topics} />
<PrivateRoute exact path='/topic/page/:pageNumber' component={Topics} />
<PrivateRoute exact path='/topic/search/:keyword/page/:pageNumber' component={Topics} />
<PrivateRoute exact path='/topic/flashcard/:id' component={Flashcard} />
<PrivateRoute exact path='/topic/quiz/:id' component={Quiz} />
<PrivateRoute exact path='/topic/factsheet/:id' component={Factsheet} />
<PrivateRoute exact path="/posts" component={Posts} />
<PrivateRoute exact path="/posts/:id" component={Post} />
<Route component={NotFound} />
</Switch>
</section>
);
};
export default Routes;
Paginate.js
import React from 'react'
import { Pagination } from 'react-bootstrap'
import { Link } from 'react-router-dom';
import { LinkContainer } from 'react-router-bootstrap'
const Paginate = ({ pages, page, keyword = '' }) => {
return (
pages > 1 && (
<Pagination>
{[...Array(pages).keys()].map((x) => (
<LinkContainer
tag={Link}
key={x + 1}
to={keyword
? `topic/search/${keyword}/page/${x + 1}`
: `topic/page/${x + 1}`
}
>
<Pagination.Item active={x + 1 === page}>{x + 1}</Pagination.Item>
</LinkContainer>
))}
</Pagination>
)
)
}
export default Paginate
SearchBox.js
import React, { useState } from 'react'
import { Form, Button } from 'react-bootstrap'
const SearchBox = ({ history }) => {
const [keyword, setKeyword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
if(keyword.trim()) {
history.push(`/topic/search/${keyword}`)
} else {
history.push('/topic')
}
}
return (
<Form onSubmit={submitHandler} inline>
<Form.Control
type="text"
name="q"
onChange={(e) => setKeyword(e.target.value)}
placeholder="Search Topics....."
></Form.Control>
</Form>
)
}
export default SearchBox
topic.js
import React, { Fragment, useEffect } from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Spinner from '../layout/Spinner';
import Paginate from '../layout/Paginate'
import TopicItem from './TopicItem';
import { getTopics } from '../../actions/topic';
const Topics = ({ getTopics, topic: { topics, loading, pages, page }, match }) => {
const keyword = match.params.keyword
const pageNumber = match.params.pageNumber || 1
useEffect(() => {
getTopics(keyword, pageNumber);
}, [getTopics, keyword, pageNumber])
return (
<Fragment>
{loading ? (
<Spinner />
) : (
<Fragment>
<h1 className="large text-primary1 my-2">Pick a Topic</h1>
<Link to="/topic" className="btn my-4">
Back To Topics
</Link>
<div className="card-grid-home">
{topics.length > 0 ? (
topics.map(topic => (
<TopicItem key={topic._id} topic={topic} />
))
) : (
<h4>No topics found......</h4>
)}
</div>
<Paginate pages={pages} page={page} keyword={keyword ? keyword : ''}/>
</Fragment>
)}
</Fragment>
)
};
Topics.prototype = {
getTopics: PropTypes.func.isRequired,
topic: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
topic: state.topic
})
export default connect(mapStateToProps, { getTopics })(Topics)
Thanks in advance
Realised that I was missing a / for the start of the routes. Silly mistake.

Unable to redirect on button click React.js (using context provider)

I'm new to React and I've been trying to redirect to a different component after getting a response from my API.
I've tried using history, location, and Redirect, but the redirect never happens.
Also, I get undefined when using all of the above.
I'm not sure if this is because my App is defined outside the Router, if it is the reason I'm still unable to fix the issue.
Here is my code:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { AppProvider } from './Context'
import { BrowserRouter as Router } from 'react-router-dom'
ReactDOM.render(
<React.StrictMode>
<AppProvider>
<App />
</AppProvider>
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React from 'react';
import './App.css';
import {Home, JoinRoom, CreateRoom, Room } from './pages';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/" exact={true}>
<Home />
</Route>
<Route path="/join">
<JoinRoom />
</Route>
<Route path="/create">
<CreateRoom />
</Route>
<Route path="/room/:roomCode">
<Room />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
Context.js
Here, in the handleRoomButtonPressed, I'm getting data from the API and trying to redirect.
import React, { useState, useContext } from 'react';
import axios from 'axios';
import { Redirect, useHistory } from "react-router-dom";
const AppContext = React.createContext()
const AppProvider = ({ children }) => {
// const history = useHistory();
const [guestCanPause, setGuestCanPause] = useState(true);
const [votesToSkip, setVotesToSkip] = useState(2);
const [isHost, setIsHost] = useState(false);
const handleVotesChange = (e) => {
e.preventDefault();
setVotesToSkip(e.target.value);
}
const handleGuestCanPauseChange = (e) => {
e.preventDefault();
setGuestCanPause(e.target.value)
}
const handleRoomButtonPressed = async (props) => {
const roomData = { guest_can_pause: guestCanPause, votes_to_skip: votesToSkip };
const response = await axios.post('/api/create-room/', roomData);
console.log(response.data)
const redirectUrl = "/room/" + response.data.code;
console.log(props)
return <Redirect to={redirectUrl} />
}
const getRoomDetails = async (roomCode) => {
axios
.get("/api/get-room?code=" + roomCode)
.then((res) => {
console.log(res.data)
setVotesToSkip(res.data.votes_to_skip);
setGuestCanPause(res.data.guest_can_pause);
setIsHost(res.data.is_host);
})
.catch((err) => console.log(err));
}
return <AppContext.Provider value={{ guestCanPause,
votesToSkip,
isHost,
handleGuestCanPauseChange,
handleVotesChange,
handleRoomButtonPressed,
getRoomDetails, }}>
{children}
</AppContext.Provider>
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
The onClick is called in CreateRoom.js
import React, { useState, } from 'react';
import { useGlobalContext } from '../Context'
import { Link } from 'react-router-dom';
import { Button, Grid, Typography, TextField, FormHelperText, FormControl, Radio, RadioGroup, FormControlLabel } from '#material-ui/core'
function CreateRoom() {
const defaultVotes = 2;
const { handleGuestCanPauseChange, handleVotesChange, handleRoomButtonPressed } = useGlobalContext();
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h4" variant="h4">
Create A Room
</Typography>
</Grid>
<Grid item xs={12} align="center">
<FormControl component="fieldset">
<FormHelperText>
<div align="center">Guest Control of Playback state</div>
</FormHelperText>
<RadioGroup row defaultValue="true" onChange={handleGuestCanPauseChange}>
<FormControlLabel value="true"
control={<Radio color="primary" />}
label="Play/Pause" labelPlacemment="bottom" />
<FormControlLabel value="false"
control={<Radio color="secondary" />}
label="No Control" labelPlacemment="bottom" />
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<FormControl>
<TextField required={true}
type="number" onChange={handleVotesChange}
defaultValue={defaultVotes}
inputProps={{ min: 1,
style: { textAlign: "center" },
}}
/>
<FormHelperText>
<div align="center">Votes Required To Skip Song</div>
</FormHelperText>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<Button
color="primary"
variant="contained"
onClick={handleRoomButtonPressed}
>
Create A Room
</Button>
</Grid>
<Grid item xs={12} align="center">
<Button color="secondary" variant="contained" to="/" component={Link}>
Back
</Button>
</Grid>
</Grid>
)
}
export default CreateRoom
If I understood a subject correctly your AppProvider is located above the router in the component tree. Thus, the react router cannot inject its dependencies into your AppProvider. If you want to access react-router API, such as useHistory hook or others, you should call it from one of the Router children, then it will works.

ReactJS Error: Element type is invalid: expected a string (for built-in components)

I am trying to export class and import it in another file, but I kept getting this error: ...
Check the render method of NewsfeedDetail.
I want to export component from this file: NewsfeedDetail.js
import React from "react";
import {
Card,
Container,
Dimmer,
Header,
Image,
Loader,
Message,
Segment,
} from "semantic-ui-react";
import { articleDetailURL, } from "../constants";
import axios from "axios";
class NewsfeedDetail extends React.Component {
state = {
loading: false,
error: null,
data: [],
};
componentDidMount() {
this.handleFetchItem();
};
handleFetchItem = () => {
const {
match: { params }
} = this.props;
this.setState({ loading: true });
axios
.get(articleDetailURL(params.articleID))
.then(res => {
this.setState({ data: res.data, loading: false });
})
.catch(err => {
this.setState({ error: err, loading: false });
});
};
render() {
const { data, error, loading } = this.state;
const item = data;
return (
<Container>
{error && (
<Message
error
header="There was some errors with your submission"
content={JSON.stringify(error)}
/>
)}
{loading && (
<Segment>
<Dimmer active inverted>
<Loader inverted>Loading</Loader>
</Dimmer>
<Image src="/images/wireframe/short-paragraph.png" />
</Segment>
)}
<Header as='h4' textAlign='center'>
<Header.Content>
{item.title}
</Header.Content>
</Header>
<Card>
<Card.Content>
<Card.Category >
{item.category}
</Card.Category>
<Card.Description>
{item.description}
</Card.Description>
<Card.Body>
{item.body}
</Card.Body>
</Card.Content>
</Card>
</Container>
);
}
};
export default NewsfeedDetail;
And I want to import it in this file: routes.js
import React from "react";
import { Route } from "react-router-dom";
import Hoc from "./hoc/hoc";
import Login from "./containers/Login";
import Signup from "./containers/Signup";
import HomepageLayout from "./containers/Home";
import ProductList from "./containers/ProductList";
import ProductDetail from "./containers/ProductDetail";
import OrderSummary from "./containers/OrderSummary";
import Checkout from "./containers/Checkout";
import Profile from "./containers/Profile";
import AboutLayout from "./containers/About";
import HotelLayout from "./containers/Hotel";
import NewsfeedList from "./containers/NewsfeedList";
import NewsfeedDetail from "./containers/NewsfeedDetail";
const BaseRouter = () => (
<Hoc>
<Route exact path="/products" component={ProductList} />
<Route path="/products/:productID" component={ProductDetail} />
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
<Route path="/order-summary" component={OrderSummary} />
<Route path="/checkout" component={Checkout} />
<Route path="/profile" component={Profile} />
<Route exact path="/" component={HomepageLayout} />
<Route exact path="/about" component={AboutLayout} />
<Route exact path="/hotel" component={HotelLayout} />
<Route exact path="/articles" component={NewsfeedList} />
<Route path="/articles/:articleID" component={NewsfeedDetail} />
</Hoc>
);
export default BaseRouter;
I have tried exporting with no default, and use {} when importing but it does not work either.
Please help me.

Categories