GraphQL only works on production when also running localhost and Apollo Client is set to localhost only - reactjs

Im working an app using react for the frontend and apollo/client and graphql. It works perfectly on the localhost, but when I deploy to heroku it only works if I also have the app running on the localhost.
Here is what I have for the uri.
const client = new ApolloClient({
uri: 'http://localhost:5000/graphql',
cache: new InMemoryCache(),
});
I tried setting the uri localhost then if in production to set it to heroku:
let uri = http://localhost:5000/graphql
if (process.env.NODE_ENV === 'production') {
uri = https://denver-judo.herokuapp.com/graphql
}
I also tried uri = https://denver-judo.herokuapp.com/${PORT}/graphql but nothing works.
This is my app.js file
import { ApolloClient, InMemoryCache, ApolloProvider } from '#apollo/client';
import { useState } from 'react';
import {
Route,
Routes,
BrowserRouter as Router,
Outlet,
} from 'react-router-dom';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import HomeScreen from './screens/HomeScreen';
import Container from 'react-bootstrap/Container';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import { LinkContainer } from 'react-router-bootstrap';
import Button from 'react-bootstrap/Button';
import AboutScreen from './screens/AboutScreen';
import AdminScreen from './screens/AdminScreen';
// Construct main GraphQL API endpoint
let uri = 'http://localhost:5000/graphql';
if (process.env.NODE_ENV === 'production') {
uri = `https://denver-judo.herokuapp.com/api/graphql`;
}
console.log(process.env.NODE_ENV);
console.log(uri);
const client = new ApolloClient({
uri: uri,
cache: new InMemoryCache(),
});
function App() {
const location = window.location.pathname;
const [sidebarIsOpen, setSidebarIsOpen] = useState(false);
return (
<ApolloProvider client={client}>
<Router>
<div
className={
sidebarIsOpen
? 'site-container active-cont d-flex flex-column'
: 'site-container d-flex flex-column'
}
>
<ToastContainer position="top-center" limit={1} />
<header>
<Navbar
expand="lg"
className={`navbar navbar-dark ${
location === '/' ? 'navbar-custom' : 'navbar-custom-light'
}`}
>
<Container>
<LinkContainer to="/">
<Navbar.Brand>
<img
className="ms-4"
src={
location === '/'
? 'assets/img/DJ-logo-black.png'
: 'assets/img/DJ-logo-white.png'
}
alt="Denver Judo"
style={{ width: '60px' }}
/>{' '}
</Navbar.Brand>
</LinkContainer>
<h1
className={
location === '/'
? 'navTitle-dark text-center'
: 'navTitle text-center'
}
>
Denver Judo
</h1>
<Button
style={{
backgroundColor: location === '/' ? '#d9d9d9' : '#262626',
}}
onClick={() => setSidebarIsOpen(!sidebarIsOpen)}
className="me-4"
>
<i
className="fa fa-bars fa-lg"
style={{
color: location === '/' ? '#262626' : '#d9d9d9',
}}
></i>
</Button>
</Container>
</Navbar>
</header>
<div
className={
sidebarIsOpen
? 'active-nav side-navbar d-flex justify-content-between flex-wrap flex-column'
: 'side-navbar d-flex justify-content-between flex-wrap flex-column'
}
>
<Nav className="flex-column text-white w-100 p-2">
<Nav.Item>
<strong>Denver Judo</strong>
</Nav.Item>
<LinkContainer to="/" onClick={() => (this.expand = '')}>
<Nav.Link
className={
location === '/' ? 'nav-text nav-text-bold' : 'nav-text'
}
>
Home
</Nav.Link>
</LinkContainer>
<LinkContainer to="/schedule" onClick={() => (this.expand = '')}>
<Nav.Link
className={
location === '/schedule'
? 'nav-text nav-text-bold'
: 'nav-text'
}
>
Schedule
</Nav.Link>
</LinkContainer>
<LinkContainer to="/about" onClick={() => (this.expand = '')}>
<Nav.Link
className={
location === '/about'
? 'nav-text nav-text-bold'
: 'nav-text'
}
>
About
</Nav.Link>
</LinkContainer>
</Nav>
<Outlet />
</div>
<main>
<Routes>
<Route index element={<HomeScreen />} />
<Route path="/about" element={<AboutScreen />} />
<Route path="_admin/*" element={<AdminScreen />} />
</Routes>
</main>
<footer>
<div className="text-center">
Denver Judo
<br />
All rights reserved
</div>
</footer>
</div>
</Router>
</ApolloProvider>
);
}
export default App;
Here is the server.js from the backend:
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const path = require('path');
const dotenv = require('dotenv');
const { typeDefs, resolvers } = require('./schemas');
const db = require('./config/connection');
require('dotenv');
dotenv.config();
const PORT = process.env.PORT || 5000;
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers,
});
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
console.log(process.env.NODE_ENV);
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '../client/build')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../client/build/index.html'));
});
}
// Create a new instance of an Apollo server with the GraphQL schema
const startApolloServer = async (typeDefs, resolvers) => {
await server.start();
server.applyMiddleware({ app });
db.once('open', () => {
app.listen(PORT, () => {
console.log(`API server running on port ${PORT}!`);
console.log(
`Use GraphQL at http://localhost:${PORT}${server.graphqlPath}`
);
});
});
};
// Call the async function to start the server
startApolloServer(typeDefs, resolvers);

It seems you are trying to access but aren't setting the /api/graphql path. Also the apollo-server-express library is depreciated. I recommend following the Migrating to Apollo Server 4 guide which highlights how to set the graphql path using app.use. Otherwise the default graphql path is / in v3. In v4 it's /graphql.
In v4 it's set like this:
app.use(
'/graphql',
cors<cors.CorsRequest>(),
json(),
expressMiddleware(server, {
context: async ({ req }) => ({ token: req.headers.token }),
}),
);

Related

Keycloak 20.0.3 logout not working in react app (invalid redirect url)

I am trying to authenticate my react app using keycloak. The app is not able to successfully logout. I have setup the admin console to contain the post logout redirect url to be http://localhost:7171. The URL that shows on clicking logout button
http://localhost:8080/realms/hype/protocol/openid-connect/logout?client_id=hypeclient&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A7171%2F&id_token_hint=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJZRHVhc0pTT0dIOUh2ZnRvZGNhRktmRkxuOEZuWmkzSVN3eVBQenlzazFVIn0.eyJleHAiOjE2NzU1NjU1ODUsImlhdCI6MTY3NTU2NTI4NSwiYXV0aF90aW1lIjoxNjc1NTY1MjcyLCJqdGkiOiI1MjEyODZlZS1hNThiLTRhMTUtYTgyZS1jODU2ZmE4NWVkZTEiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL2h5cGUiLCJhdWQiOiJoeXBlY2xpZW50Iiwic3ViIjoiYzJhZTJiZWYtMjg2ZC00MDgzLTk3YTktMWEyM2YxNzQ4YzE0IiwidHlwIjoiSUQiLCJhenAiOiJoeXBlY2xpZW50Iiwibm9uY2UiOiI3NGUyMTk3Ni0zNzcwLTQzNWEtOTEzMC1lNGU2YWUyY2I4ZTIiLCJzZXNzaW9uX3N0YXRlIjoiYzAxZWJlYTEtMjAwYS00YzU3LTg2NTgtNjYyYTdjOTA5OThkIiwiYXRfaGFzaCI6IjNPczlxc0NucFdmYnFuRkNlZ1docmciLCJhY3IiOiIwIiwic2lkIjoiYzAxZWJlYTEtMjAwYS00YzU3LTg2NTgtNjYyYTdjOTA5OThkIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoia2FyYW4gc2hhaCIsInByZWZlcnJlZF91c2VybmFtZSI6ImthcmFuIiwiZ2l2ZW5fbmFtZSI6ImthcmFuIiwiZmFtaWx5X25hbWUiOiJzaGFoIn0.TFCh-dPE4h8HKpAapNv4socR7uAsKDnuVpqLiaZCguu1Vyztn_sUkrbaqFKJDeoIZv3XDzBDM5MezMvyRRkmiRIQTIbiTlYGgcNA5UkcwbeKioaowCWqYqyjG7A3D0lKNjf0MAF6PmWVrPJVbm8hYtQMVDHwWQ2vFTShnmMRJ2iTf4IeT1DDLANrLh3tMXeYMF2f9ZmvtNS72O-BICkD4uXPSyMiqnDgj_52UAdkEaLgkuv31TAYDAeVef-Rly8n7K5LG1MWytB2_-WrhwXwfxjymc9RH55Iy-1tvLUFceigO7XwQzffZYK5uJ9PXkbg3hlkr6QLVSXsiu8RYKQu9w
My Setup including the version looks like
Keycloak 20.0.3
Keycloak.js 20.0.3 via npm
react 18.2
App.JS
import React from "react";
import { ReactKeycloakProvider } from "#react-keycloak/web";
import keycloak from "./Keycloak";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Nav from "./components/Nav";
import WelcomePage from "./pages/Homepage";
import SecuredPage from "./pages/Securedpage";
import PrivateRoute from "./helpers/PrivateRoute";
function App() {
return (
<div>
<ReactKeycloakProvider authClient={keycloak}>
<Nav />
<BrowserRouter>
<Routes>
<Route exact path="/" element={<WelcomePage />} />
<Route path="/secured"
element={<PrivateRoute>
<SecuredPage />
</PrivateRoute>}/>
</Routes>
</BrowserRouter>
</ReactKeycloakProvider>
</div>
);
}
export default App;
Nav.JS
import React from "react";
import { useKeycloak } from "#react-keycloak/web";
const Nav = () => {
const { keycloak, initialized } = useKeycloak();
function loginHelper(){
console.log("login clicked");
keycloak.login();
};
function logoutHelper(){
console.log("login clicked");
keycloak.logout();
};
return (
<div>
<div className="top-0 w-full flex flex-wrap">
<section className="x-auto">
<nav className="flex justify-between bg-gray-200 text-blue-800 w-screen">
<div className="px-5 xl:px-12 py-6 flex w-full items-center">
<h1 className="text-3xl font-bold font-heading">
Keycloak React AUTH.
</h1>
<ul className="hidden md:flex px-4 mx-auto font-semibold font-heading space-x-12">
<li>
<a className="hover:text-blue-800" href="/">
Home
</a>
</li>
<li>
<a className="hover:text-blue-800" href="/secured">
Secured Page
</a>
</li>
</ul>
<div className="hidden xl:flex items-center space-x-5">
<div className="hover:text-gray-200">
{!keycloak.authenticated && (
<button
type="button"
className="text-blue-800"
onClick={() => loginHelper()}
>
Login
</button>
)}
{!!keycloak.authenticated && (
<button
type="button"
className="text-blue-800"
onClick={() => logoutHelper()}
>
Logout ({keycloak.tokenParsed.preferred_username})
</button>
)}
</div>
</div>
</div>
</nav>
</section>
</div>
</div>
);
};
export default Nav;
Keycloak.JS
import Keycloak from "keycloak-js";
const keycloak = new Keycloak({
url: "http://localhost:8080",
realm: "hype",
clientId: "hypeclient",
});
export default keycloak;
PrivateRoute.JS
import { useKeycloak } from "#react-keycloak/web";
const PrivateRoute = ({ children }) => {
const { keycloak } = useKeycloak();
const isLoggedIn = keycloak.authenticated;
console.log("checking auth access " + isLoggedIn);
return isLoggedIn ? children : null;
};
export default PrivateRoute;
I have tried following this link but it does not work. Admin console looks like
Try changing
valid post logout urls to +
valid redirect urls to http://localhost:7171*

How to fix problems with navigating to different pages by ID in a React app?

I am having trouble navigating to different pages based on ID. I am making a recipe app with the MERN Stack.
I keep getting errors like No routes matched location "/api/recipes/61f9626a6b8d175f6b6dc725"
Here is my code:
App.js
import React from 'react';
import {BrowserRouter as Router, Route, NavLink, Routes} from 'react-router-dom';
import './App.css';
import Home from './components/pages/Home';
import RecipeList from './components/recipes/RecipeList';
import RecipeInfo from './components/recipes/RecipeInfo';
import RecipeAdd from './components/recipes/RecipeAdd';
import RecipeEdit from './components/recipes/RecipeEdit';
function App() {
return (
<div className="App">
<Router>
<Navigation />
<div className="container">
<Main />
</div>
</Router>
</div>
);
}
function Navigation() {
return(
<nav className="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
<div className='container'>
<ul className="navbar-nav mr-auto">
<li className="nav-item"><NavLink exact="true" className="nav-link" to="/">Home</NavLink></li>
<li className="nav-item"><NavLink exact="true" className="nav-link" to="/recipes">Recipes</NavLink></li>
</ul>
</div>
</nav>
);
}
function Main() {
return(
<Routes>
<Route path="/recipes" element={<RecipeList />} />
<Route path="/recipes/new" element={<RecipeAdd />} />
<Route path="/recipes/:id" element={<RecipeInfo />} />
<Route path="/recipes/:id/edit" element={<RecipeEdit />} />
<Route exact path="/" element={<Home />} />
</Routes>
);
}
export default App;
RecipeInfo.js
import React, { useState, useEffect } from "react";
import axios from 'axios';
import { Link, useParams, useNavigate } from 'react-router-dom';
function RecipeInfo(props) {
const [recipe, setRecipe] = useState({});
const { _id } = useParams();
const navigate = useNavigate();
useEffect(function() {
async function getRecipe() {
try {
const response = await axios.get(`/api/recipes/${_id}/`);
setRecipe(response.data);
} catch(error) {
console.log('error', error);
}
}
getRecipe();
}, [props, _id]);
async function handleDelete() {
try {
await axios.delete(`/api/recipes/${_id}/`);
navigate("/api/recipes");
} catch(error) {
console.error(error);
}
}
return (
<div>
<h2>{recipe.title}</h2>
<small>_id: {recipe._id}</small>
<p>{recipe.ingredients}</p>
<p>{recipe.procedure}</p>
<div className="btn-group">
<Link to={`/api/recipes/${recipe._id}/edit`} className="btn btn-primary">Edit</Link>
<button onClick={handleDelete} className="btn btn-danger">Delete</button>
<Link to="/api/recipes" className="btn btn-secondary">Close</Link>
</div>
<hr/>
</div>
);
};
export default RecipeInfo;
Server.js
const express = require('express');
const mongoose = require('mongoose');
// const cors = require('cors');
const router = require('./routes/index');
const app = express();
const PORT = 3001;
const MONGODB_URI = "mongodb://localhost:27017/recipeDB";
// app.use(cors())
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/api', router);
mongoose.connect(MONGODB_URI, {});
mongoose.connection.once('open', function() {
console.log('Connected to the Database.');
});
mongoose.connection.on('error', function(error) {
console.log('Mongoose Connection Error: ' + error);
});
app.listen(PORT, function() {
console.log(`Server listening on port ${PORT}.`);
});
I am also getting an error saying error TypeError: Cannot read properties of undefined (reading 'push') at postRecipe (RecipeAdd.js:23:1) Here is the code for that file.
RecipeAdd.js
import React, { useState } from "react";
import { post } from 'axios';
import { useNavigate } from "react-router-dom";
function RecipeAdd(props) {
const initialState = { title: '', content: '' }
const [recipe, setRecipe] = useState(initialState)
const navigate = useNavigate();
function handleChange(event) {
setRecipe({...recipe, [event.target.name]: event.target.value})
}
function handleSubmit(event) {
event.preventDefault();
if(!recipe.title || !recipe.ingredients || !recipe.procedure) return
async function postRecipe() {
try {
const response = await post('/api/recipes', recipe);
props.history.push(`/recipes/${response.data._id}`);
} catch(error) {
console.log('error', error);
}
}
postRecipe();
navigate("/recipes")
}
function handleCancel() {
navigate("/recipes");
}
return (
<div>
<h1>Create Recipe</h1>
<hr/>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Title</label>
<input name="title" type="text" value={recipe.title} onChange={handleChange} className="form-control" />
</div>
<div className="form-group">
<label>Ingredients</label>
<textarea name="ingredients" rows="5" value={recipe.ingredients} onChange={handleChange} className="form-control" />
</div>
<div className="form-group">
<label>Procedure</label>
<textarea name="procedure" rows="5" value={recipe.procedure} onChange={handleChange} className="form-control" />
</div>
<div className="btn-group">
<input type="submit" value="Submit" className="btn btn-primary" />
<button type="button" onClick={handleCancel} className="btn btn-secondary">Cancel</button>
</div>
</form>
</div>
);
}
export default RecipeAdd;
I am fairly new to React, so any advice or best practices I could be doing would be greatly appreciated. Thanks!
Those errors seems to be valid.
Normal navigation around the app do not need to have /api/* before. Reason I think is because these are API routes and not navigation routes.
RecipeInfo.js
...
async function handleDelete() {
try {
await axios.delete(`/api/recipes/${_id}/`);
navigate("/recipes");
} catch(error) {
console.error(error);
}
}
...
return (
...
<Link to={`/recipes/${recipe._id}/edit`} className="btn btn-primary">Edit</Link>
<button onClick={handleDelete} className="btn btn-danger">Delete</button>
<Link to="/recipes" className="btn btn-secondary">Close</Link>
...
)
...
For the type error. Consider using navigate for routing instead of props being passed.
RecipeAdd.js line 23
// props.history.push(`/recipes/${response.data._id}`);
navigate(`/recipes/${response.data._id}`);

React with Router v5 Error: Objects are not valid as a React child (found: object with keys {children})

I'm new to react but Im trying to create a web app that is essentially 2 apps in one. By 2 apps in one I mean I have 2 separate layouts, one when authorized and one when not. I'm running into a problem when logging in currently, when I try to redirect on successful log in I get the following error:
Error: Objects are not valid as a React child (found: object with keys {children}). If you meant to render a collection of children, use an array instead.
I'm sure this has something to do with how I have my routes set up or how Im redirecting.
Heres all my code. The error is usually pointing to the history.push line in login.js in the handlesubmit function. Note: my code is actually split into multiple js files for each function, I just combined them here so the code would be a bit more compact (I also combined the imports just for this example).
Update: I think I narrowed down my problem to my ProtectedRoute component, but I still dont totally know what the problem is. I think its how Im passing in an array of paths to that component but Im not sure how to fix it.
import React, { useState,useEffect } from "react";
import { NavLink, Route, Switch, useRouteMatch, useHistory, useLocation, useParams } from 'react-router-dom';
import MainLayout from "../layouts/MainLayout";
import AuthLayout from "../layouts/AuthLayout";
import NotFound from "../pages/NotFound";
import Login from "../pages/Login";
import Welcome from "../pages/Welcome";
import Dashboard from "../pages/Dashboard";
import Locations from "../pages/Locations";
import ProtectedRoute from "./ProtectedRoute";
import Navbar from "react-bootstrap/Navbar";
import Nav from "react-bootstrap/Nav";
import { LinkContainer } from "react-router-bootstrap";
import { ReactComponent as Logo } from '../images/Logo.svg';
import { useAppContext } from "../libs/contextLib";
import { LinkContainer } from "react-router-bootstrap";
import { useAppContext } from "../libs/contextLib";
import axios from 'axios';
import Form from "react-bootstrap/Form";
import LoaderButton from "../components/LoaderButton";
import LoginImage from '../images/Login-Page-Image.png';
import FloatLabelTextBox from "../components/FloatLabelTextBox.js"
import { API_BASE_URL, ACCESS_TOKEN_NAME } from '../constants/apiConstants.js';
import { onError } from "../libs/errorLib";
export default function MainRoutes() {
return (
<Switch>
<Route path={['/login', '/welcome']}>
<AuthLayout>
<Route path='/login' component={Login} />
<Route path='/welcome' component={Welcome} />
</AuthLayout>
</Route>
<ProtectedRoute exact path={['/', '/locations']}>
<MainLayout>
<Route path='/locations' component={Locations} />
<Route exact path='/' component={Dashboard} />
</MainLayout>
</ProtectedRoute>
{/* Finally, catch all unmatched routes */}
<Route>
<NotFound />
</Route>
</Switch>
);
}
function AuthLayout({children}) {
const { isAuthenticated } = useAppContext();
return (
<>
<div className="AuthLayout container py-3">
<Navbar collapseOnSelect expand="md" className="mb-3 login-nav">
<LinkContainer to="/welcome">
<Navbar.Brand href="/welcome" className="font-weight-bold text-muted">
<Logo />
</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle />
<Navbar.Collapse className="justify-content-end">
<Nav activeKey={window.location.pathname}>
<LinkContainer to="/welcome">
<Nav.Link>Home</Nav.Link>
</LinkContainer>
<LinkContainer to="/login">
<Nav.Link>Login</Nav.Link>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Navbar>
<div className="Auth-Layout-Body">
{children}
</div>
</div>
</>
);
}
export default AuthLayout;
function MainLayout({ children }) {
const { isAuthenticated } = useAppContext();
const { userHasAuthenticated } = useAppContext();
const history = useHistory();
function handleLogout() {
userHasAuthenticated(false);
console.log("log out");
history.push("/login");
}
return (
<>
<div className="MainLayout container py-3">
<Navbar collapseOnSelect expand="md" className="mb-3 login-nav">
<LinkContainer to="/">
<Navbar.Brand href="/" className="font-weight-bold text-muted">
Location INTEL
</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle />
<Navbar.Collapse className="justify-content-end">
<Nav activeKey={window.location.pathname}>
<LinkContainer to="/">
<Nav.Link>Home</Nav.Link>
</LinkContainer>
<LinkContainer to="/locations">
<Nav.Link>Locations</Nav.Link>
</LinkContainer>
{isAuthenticated ? (
<Nav.Link onClick={handleLogout}>Logout</Nav.Link>
) : (<div></div>)}
</Nav>
</Navbar.Collapse>
</Navbar>
<div className="Main-Layout-Body">
{children}
</div>
</div>
</>
);
}
export default MainLayout;
export default function Login() {
const history = useHistory();
const [state, setState] = useState({
email: "",
password: "",
});
const { userHasAuthenticated } = useAppContext();
const [isLoading, setIsLoading] = useState(false);
const handleChange = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
})
}
function validateForm() {
return state.email.length > 0 && state.password.length > 0;
}
function handleSubmit(event) {
event.preventDefault();
setIsLoading(true);
const payload = {
"email": state.email,
"password": state.password,
}
try {
axios.post('/api/user/login', payload, {
headers: {
useCredentails: true,
'x-api-key': ACCESS_TOKEN_NAME,
"Access-Control-Allow-Origin": "*"
}
})
.then(function (response) {
console.log(response);
//console.log('status code = ' + response.status);
if (response.status === 200) {
console.log("logged in");
userHasAuthenticated(true);
history.push("/");
} else {
console.log("not logged in");
}
})
.catch(function (error) {
console.log(error);
});
} catch (e) {
onError(e);
setIsLoading(false);
}
}
return (
<div className="Login-Container">
<div className="Login-Container-Row">
<div className="Login">
<p className="Login-Header">Login</p>
<div className="Login-Form">
<Form onSubmit={handleSubmit}>
<Form.Group size="lg" controlId="email">
<FloatLabelTextBox
inputLabel="EMAIL"
inputAutoFocus="autofocus"
inputType="email"
inputName="email"
inputPlaceholder="Email"
inputValue={state.email}
handleChangeProps={handleChange}
/>
</Form.Group>
<Form.Group size="lg" controlId="password">
<FloatLabelTextBox
inputLabel="PASSWORD"
inputAutoFocus=""
inputType="password"
inputName="password"
inputPlaceholder="Password"
inputValue={state.password}
handleChangeProps={handleChange}
/>
</Form.Group>
<LoaderButton
block
size="lg"
type="submit"
isLoading={isLoading}
disabled={!validateForm()}>
Login
</LoaderButton>
<p>Not a member? <NavLink to="/register">Get Started Here</NavLink></p>
</Form>
</div>
</div>
<div className="Login-Image">
<img src={LoginImage} />
</div>
</div>
</div>
);
}
export default function ProtectedRoute({ children, ...props }) {
const { isAuthenticated } = useAppContext();
return (
<Route
{...props}
render={props => (
isAuthenticated ?
{children} :
<Redirect to='/login' />
)}
/>
);
}
Your problem is here:
<Route
{...props}
render={props => (
isAuthenticated ?
{children} : // <=== HERE!
<Redirect to='/login' />
)}
You are using {children} as if you were in "JSX-land" ... but you're not. You're already inside a set of {} in your JSX ... which means you're in "Javascript-land".
In "Javascript-land", {children} means "make me an object with a single child property called children". When you then try to insert that object into your JSX, React doesn't know what to do with it, and you get your error.
You just want the same code, minus those curly braces:
<Route
{...props}
render={props => (
isAuthenticated ?
children :
<Redirect to='/login' />
)}
P.S. Don't forget that route tags are just tags, so if you're doing this pattern a lot you might want to create an AuthenticatedRoute tag and use it (instead of repeating this ternary).

getting "Missing required parameter 'client_id'" error in react-google-login

I inserted my client id correctly and it did work
but suddenly it's not working until now even though I didn't change any codes in <GoogleLogin />
Also it works in blank React when I tested but not working in this project
exact error message is Uncaught qw {message: "Missing required parameter 'client_id'", xM: true, stack: "gapi.auth2.ExternallyVisibleError: Missing require…YZKdHMHzzbYNF1G7KSEIjXaBQQw/cb=gapi.loaded_0:1:15"}
import React, { useState } from "react";
import "./CSS/Header.css";
import { BrowserRouter as Router, Link } from "react-router-dom";
import { GoogleLogin } from "react-google-login";
export default function Header() {
const responseGoogle = (response) => {
console.log(response);
};
return (
<Router>
<div id="header">
<div id="id"></div>
<h2 id="header_title">title</h2>
<div id="contents">
<h4 className="content">
<Link
to="/"
onClick={() => {
window.location.href = "/";
}}
>
HOME
</Link>
</h4>
<h4 className="content">
<Link
to="/history"
onClick={() => {
window.location.href = "/history";
}}
>
History
</Link>
</h4>
<GoogleLogin
clientid=[myclientid]
buttonText="Login"
onSuccess={responseGoogle}
isSignedIn={true}
onFailure={responseGoogle}
cookiePolicy={"single_host_origin"}
/>
</div>
</div>
</Router>
);
}
You should use clientId instead of clientid for the prop

React update state without refresh

I'm trying to update the state in a navigation component to show "Log Out" if a cookie is set. The only problem is I cannot get the cookie without refreshing the entire page. Is there a way for me to get the cookie without a refresh? The below code shows how I'm trying to get the cookie and setting the state.
Updated with code:
import React, {Component} from 'react';
import './NavigationItems.css';
import Logo from '../../Logo/Logo';
import { Route, Link } from 'react-router-dom';
import PortalFindSubscriber from '../../../containers/Portal/PortalFindSubscriber';
import Login from "../../../components/Login/Login";
import Register from "../../../components/Register/Register";
import Reset from "../../../components/Reset/Reset";
import ResetConfirm from "../../../components/Reset/ResetConfirm";
import Aux from '../../../hoc/Aux/Aux';
import Cookies from 'universal-cookie';
const cookies = new Cookies();
class navigationItems extends Component{
constructor() {
super();
this.updateLoggedStatus = this.updateLoggedStatus.bind(this)
}
state = {
isLogged: false,
};
componentDidMount() {
this.setState({isLogged: true});
console.log(this.state.isLogged);
this.updateLoggedStatus();
}
Collapse (e) {
const body = document.body;
const collapseBtn = e.target;
const collapsedClass = "collapsed";
collapseBtn.getAttribute("aria-expanded") === "true"
? collapseBtn.setAttribute("aria-expanded", "false")
: collapseBtn.setAttribute("aria-expanded", "true");
collapseBtn.getAttribute("aria-label") === "collapse menu"
? collapseBtn.setAttribute("aria-label", "expand menu")
: collapseBtn.setAttribute("aria-label", "collapse menu");
body.classList.toggle(collapsedClass);
};
Toggle(e){
const body = document.body;
const toggleMobileMenu = e.target;
toggleMobileMenu.getAttribute("aria-expanded") === "true"
? toggleMobileMenu.setAttribute("aria-expanded", "false")
: toggleMobileMenu.setAttribute("aria-expanded", "true");
toggleMobileMenu.getAttribute("aria-label") === "open menu"
? toggleMobileMenu.setAttribute("aria-label", "close menu")
: toggleMobileMenu.setAttribute("aria-label", "open menu");
body.classList.toggle("mob-menu-opened");
}
onSignOutClick = () =>{
cookies.remove('AccessToken');
this.setState({isLogged: false});
}
updateLoggedStatus() {
this.setState({ isLogged: true });
}
render () {
cookies.addChangeListener(console.log(cookies.get('AccessToken')));
console.log(this.state.isLogged);
//const { isLogged } = this.state;
return(
<Aux>
<header className="pageHeader">
<nav>
<Link to={"/"}>
<Logo />
</Link>
<button className="toggle-mob-menu" aria-expanded="false" aria-label="open menu" onClick={this.Toggle}>
<svg width="20" height="20" aria-hidden="true">
<use xlinkHref="#down"></use>
</svg>
</button>
<ul className="adminMenu">
<li className="menuHeading">
<h3>Admin</h3>
</li>
{cookies.get('AccessToken')!==undefined?
<>
<li>
<Link to={"/"} onClick={this.onSignOutClick}>
<div class="material-icons" style={{marginRight: 10}}>
login
</div>
<span>Log Out</span>
</Link>
</li>
</>
:<li>
<Link to={"/"}>
<svg>
<use xlinkHref="#users"></use>
</svg>
<span>Log In</span>
</Link>
</li>}
<li>
<button className="collapse-btn" aria-expanded="true" aria-label="collapse menu" onClick={this.Collapse}>
<svg aria-hidden="true">
<use xlinkHref="#collapse"></use>
</svg>
<span>Collapse</span>
</button>
</li>
</ul>
</nav>
</header>
<Route exact path="/" component={Login} />
<Route exact path="/terms" component={Register} />
<Route exact path="/reset" component={Reset} />
<Route exact path="/reset_confirm" component={ResetConfirm} />
<Route exact path="/find" component={PortalFindSubscriber} />
</Aux>
);
}
}
export default navigationItems;
You Should use
boolean shouldComponentUpdate(object nextProps, object nextState)
which decided whether the component should be updated or not.
for feasible solution you can use Functional Components's UseEffect callbacks for updating states

Resources