I have wrapped my main App components with the provider
...
import { QueryClient, QueryClientProvider } from "react-query";
/**
* The main app which handles the initialization and routing
* of the app.
*/
const queryClient = new QueryClient();
export default function App() {
const { loading, theme, themeString, teamsfx } = useTeamsFx();
if (teamsfx) {
const scope = [...
];
const provider = new TeamsFxProvider(teamsfx, scope);
Providers.globalProvider = provider;
Providers.globalProvider.setState(ProviderState.SignedIn);
}
return (
<QueryClientProvider client={queryClient} contextSharing={true}>
<TeamsFxContext.Provider value={{ theme, themeString, teamsfx }}>
<FluentProvider theme={teamsLightTheme}>
<Provider
theme={theme || teamsTheme}
styles={{ backgroundColor: "#eeeeee" }}
>
<Router>
<Route exact path="/">
<Redirect to="/tab" />
</Route>
{loading ? (
<Loader style={{ margin: 100 }} />
) : (
<>
<Route exact path="/privacy" component={Privacy} />
<Route exact path="/termsofuse" component={TermsOfUse} />
<Route exact path="/tab" component={Tab} />
<Route
exact
path="/tab/organizer/assignTests"
component={TestsAffect}
/>
<Route
exact
path="/tab/organizer/candidatsStatus"
component={CandidatsStatus}
/>
<Route
exact
path="/tab/candidat/TestsSelect"
component={CandidatTestSelect}
/>
<Route
exact
path="/tab/candidat/passTests/:language/:difficulty"
component={TestPassing}
/>
<Route
exact
path="/tab/organizer/createTest"
component={CreateTest}
/>
<Route exact path="/config" component={TabConfig} />
</>
)}
</Router>
</Provider>
</FluentProvider>
</TeamsFxContext.Provider>
</QueryClientProvider>
);
}
Component where I use useQuery
export function CandidatsStatus() {
const [customers, setCustomers] = useState<any>([]);
const { teamsfx } = useContext(TeamsFxContext);
const history = useHistory();
const { data, isLoading, isError } = useQuery({
queryKey: ["customTests"],
queryFn: () =>
fetch(`${BASE_URL}${API_URLS.TEST}`).then((res) => res.json()),
});
return (
<>
...
</>
);
}
But I'm still getting
No QueryClient set, use QueryClientProvider to set one
I don't know where is the issue even after cheking similar questions
Related
I tried to solve this problem in several ways without success. This Router work perfect with render but when I replace by element then the App.jsx throws me an error.
If I delete a () => in Route with Homepage than Homepage component will be render, but I can't do this in Route with signin/signup.
I can't figure out where the error could be.
import React, { useState, useEffect } from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import Homepage from "./Pages/Homepage/Homepage";
import SignIn from "./Pages/Authentication/SignIn";
import SignUp from "./Pages/Authentication/SignUp";
import Dashboard from "./Pages/Dashboard/Dashboard";
import "react-toastify/dist/ReactToastify.css";
import { ToastContainer } from "react-toastify";
const App = () => {
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const checkAuthenticated = async () => {
try {
const res = await fetch("/api/auth/verify", {
method: "POST",
headers: { jwtToken: localStorage.token },
});
const parseRes = await res.json();
parseRes === true ? setIsAuthenticated(true) : setIsAuthenticated(false);
setIsLoading(false);
} catch (err) {
console.error(err.message);
}
};
useEffect(() => {
checkAuthenticated();
}, []);
const setAuth = (boolean) => {
setIsAuthenticated(boolean);
};
return (
<>
{isLoading ? null : (
<BrowserRouter>
<Routes>
<Route
exact
path="/signin"
element={(props) =>
!isAuthenticated ? (
<SignIn {...props} setAuth={setAuth} />
) : (
<Navigate to="/dashboard/overview" />
)
}
/>
<Route
exact
path="/signup"
element={((props) =>
!isAuthenticated ? (
<SignUp {...props} setAuth={setAuth} />
) : (
<Navigate to="/dashboard/overview" />
)
)}
/>
<Route
path="/dashboard"
element={(props) =>
isAuthenticated ? (
<Dashboard {...props} setAuth={setAuth} />
) : (
<Navigate to="/signin" />
)
}
/>
<Route exact path="/" element={() => <Homepage />} />
</Routes>
</BrowserRouter>
)}
</>
);
};
export default App;
The Route component changed significantly from react-router-dom#5 to react-router-dom#6. There are no longer component or render or children function props, instead replaced by a single element prop taking a ReactNode, a.k.a. JSX.
const App = () => {
...
return (
<>
{isLoading ? null : (
<BrowserRouter>
<Routes>
<Route
path="/signin"
element={!isAuthenticated
? <SignIn setAuth={setAuth} />
: <Navigate to="/dashboard/overview" />
}
/>
<Route
path="/signup"
element={!isAuthenticated
? <SignUp setAuth={setAuth} />
: <Navigate to="/dashboard/overview" />
)}
/>
<Route
path="/dashboard"
element={isAuthenticated
? <Dashboard setAuth={setAuth} />
: <Navigate to="/signin" />
}
/>
<Route path="/" element={<Homepage />} />
</Routes>
</BrowserRouter>
)}
</>
);
};
export default App;
Route protection is so common that there is layout route pattern for it. Create layout routes that consume the isAuthenticated state and render null or some loading indicator while the authentication status is fetched, then conditionally renders an Outlet for the nested routes to render their content into or a redirect.
Example:
import { Navigate, Outlet } from 'react-router-dom';
const ProtectedRoutes = ({ isAuthenticated }) => {
if (isAuthenticated === undefined) {
return null; // or loading indicator, etc..
}
return isAuthenticated
? <Outlet />
: <Navigate to="/signin" replace />;
}
const AnonymousRoutes = ({ isAuthenticated }) => {
if (isAuthenticated === undefined) {
return null; // or loading indicator, etc..
}
return isAuthenticated
? <Navigate to="/dashboard/overview" replace />
: <Outlet />;
}
...
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(); // initially undefined
useEffect(() => {
const checkAuthenticated = async () => {
try {
const res = await fetch("/api/auth/verify", {
method: "POST",
headers: { jwtToken: localStorage.token },
});
const parseRes = await res.json();
setIsAuthenticated(!!parseRes);
} catch (err) {
console.error(err.message);
}
};
checkAuthenticated();
}, []);
const setAuth = (boolean) => {
setIsAuthenticated(boolean);
};
return (
<BrowserRouter>
<Routes>
<Route element={<AnonymousRoutes isAuthenticated={isAuthenticated} />}>
<Route path="/signin" element={<SignIn setAuth={setAuth} />} />
<Route path="/signup" element={<SignUp setAuth={setAuth} />} />
</Route>
<Route element={<ProtectedRoutes isAuthenticated={isAuthenticated} />}>
<Route path="/dashboard" element={<Dashboard setAuth={setAuth} />} />
</Route>
<Route path="/" element={<Homepage />} />
</Routes>
</BrowserRouter>
);
};
export default App;
Note also that because there are no render functions for the routed components that there are no longer any route props (i.e. history, location, match). These were replaced by React hooks, i.e. useNavigate, useLocation, and useParams. Use the hooks the components if they need to access these.
I am struggling to render my routes list with React router v6.
I have already read the docs and I am aware of a new structure of routing, but currently having a problem rendering list (nested) of routes.
export default [{
path: '/',
element: Root,
routes: [{
path: REGISTER,
element: Register,
isProtected: true,
}],
}];
export const RouteMatcher = ({ routes }) => {
return routes.map((route, index) => {
if (route.element) {
return (
<Route key={index} path={route.path} element={<route.element />}>
{route.routes ? (
<Route path={route.path} element={<RouteMatcher routes={route.routes} />
) : null}
</Route>
);
}
return null;
});
};
<BrowserRouter>
<Routes>
<Route path="/" element={<RouteMatcher routes={routes} />} />
</Routes>
</BrowserRouter>
It's not obvious to me what's going on here even though the error message has a clear explanation of the issue.
Even I try this way it doesn't work.
<BrowserRouter>
<Routes>
<Route path="/">
{routes.map((route, index) => {
if (route.element) {
return (
<Route key={index} path={route.path} element={<route.element />}>
{route.routes ? (
<Route path={route.path} element={<RouteMatcher routes={route.routes} indexPathname={indexPathname} />} />
) : null}
</Route>
);
}
return null;
})}
</Route>
</Routes>
</BrowserRouter>
As you can see Route is always a child of Routes or Route (when nested).
** UPDATED **
Here is the react router v5 implementation
<Provider store={store}>
<GlobalStyles />
<AppContentWrapper>
<RouteHandler />
</AppContentWrapper>
</Provider>
Route handler component
<BrowserRouter>
{generateRouteMatches(
routes,
indexPathname,
auth.isLoading,
auth.isLoggedIn
)}
</BrowserRouter>
Route generator component
export const generateRouteMatches = (
baseRoutes: IAppRoute[],
indexPathname: string,
userPermissions: PermissionShape[],
isLoading: boolean,
isLoggedIn: boolean,
) => {
AccessControl.createPermissions(userPermissions);
return baseRoutes.map((route) => (
<MatchRoutes
indexPathname={indexPathname}
authIsLoading={isLoading}
isLoggedIn={isLoggedIn}
key={route.path}
{...route}
/>
));
};
MatchRoutes component with RouteRenderer
function MatchRoutes({ location, ...route }: any) {
const routePermissions = AccessControl.getPermissions(route.zone);
if (!routePermissions.canRead && route.isProtectedRoute) {
return <Route {...omit(route, ['component'])} component={() => {
return <div className="centerXY">You dont have permissions to view this page</div>;
}} />;
}
return (
<Route {...omit(route, ['component'])} render={(props) => (
<RouteRenderer {...props} route={route} location={location} />
)} />
);
}
function RouteRenderer({ route, ...props }: any) {
const location = useLocation();
if (location?.pathname === '/') {
return (
<Redirect
to={{
pathname: route.indexPathname,
state: { from: location },
}}
/>
);
}
if (route.isProtectedRoute && !route.isLoggedIn && !route.authIsLoading) {
return (
<Redirect to={{
pathname: '/login',
state: { from: location },
}}/>
);
}
if (route.component) {
return (
<route.component
{...props}
params={props.match.params}
routes={route.routes}
>
{route.routes
? route.routes.map((cRoute, idx) => (
<MatchRoutes
authIsLoading={route.authIsLoading}
isLoggedIn={route.isLoggedIn}
key={idx}
{...cRoute}
/>
))
: null
}
</route.component>
);
} else if (route.routes) {
return (
<>
{route.routes.map((cRoute, idx) => (
<MatchRoutes
authIsLoading={route.authIsLoading}
isLoggedIn={route.isLoggedIn}
key={idx}
{...cRoute}
/>
))}
</>
);
} else {
return null;
}
}
export default MatchRoutes;
In the first example the RouteMatcher component is rendering a Route component directly. The mapped routes it is rendering need to be wrapped in a Routes component.
export const RouteMatcher = ({ routes }) => {
return (
<Routes>
{routes
.filter(route => route.element)
.map((route, index) => {
return (
<Route
key={index}
path={route.path}
element={<route.element />}
>
{route.routes && (
<Route
path={route.path}
element={<RouteMatcher routes={route.routes} />}
/>
)}
</Route>
);
})
}
</Routes>
);
};
I suspect something similar is occurring int he second code example as well.
I suggest using a better formed routes configuration and use the useRoutes hook.
Example:
export default [{
path: '/',
element: <Root />,
children: [
{
element: <AuthOutlet />,
children: [
{
path: REGISTER,
element: <Register />,
},
... other routes to protect ...
],
... other unprotected routes ...
},
],
}];
...
import appRoutes from '../path/to/routes';
...
const routes = useRoutes(appRoutes);
...
<BrowserRouter>
{routes}
</BrowserRouter>
I am learning connecting MongoDB Realm to react by following this article. The problem with this article is that it is outdated, and the newer version of react doesn't support component = {Home} in react-router and perhaps not the render = {()={}} also.
When I shamelessly copy-pasted all code and then ran it I got this warning
index.js:21 Matched leaf route at location "/" does not have an element. This means it will render an with a null value by default resulting in an "empty" page.
then I changed the code(a line) for the Home page just for testing to this
<Route path="/" element={()=><MongoContext.Consumer>{(mongoContext) => <Home mongoContext={mongoContext}/>}</MongoContext.Consumer>} />
Then I got a new warning, LOL!
Warning
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
I have no what to do now. So, if anyone knows how to solve this, then it will be helpful for me.
App.js
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from "./pages/Home"
import * as Realm from 'realm-web'
import Authentication from './pages/Authentication';
import LogOut from './pages/Logout';
import Navigation from './components/Navigation';
import MongoContext from './MongoContext';
import 'bootstrap/dist/css/bootstrap.min.css'
import { Container } from "react-bootstrap"
import { useEffect, useState } from 'react';
function renderComponent (Component, additionalProps = {}) {
return <MongoContext.Consumer>{(mongoContext) => <Component mongoContext={mongoContext} {...additionalProps} />}</MongoContext.Consumer>
}
function App() {
const [client, setClient] = useState(null)
const [user, setUser] = useState(null)
const [app, setApp] = useState(new Realm.App({ id: "restaurant_app-qbafd" }))
useEffect(() => {
async function init() {
if (!user) {
setUser(app.currentUser ? app.currentUser : await app.logIn(Realm.Credentials.anonymous()))
}
if (!client) {
setClient(app.currentUser.mongoClient('mongodb-atlas'))
}
}
init();
}, [app, client, user])
return (
<BrowserRouter>
<Navigation user={user} />
<MongoContext.Provider value={{ app, client, user, setClient, setUser, setApp }}>
<div className="App">
<header className="App-header">
<Routes>
<Route path="/signup" render={() => renderComponent(Authentication, {type: 'create'})} />
<Route path="/signin" render={() => renderComponent(Authentication)} />
<Route path="/logout" render={() => renderComponent(LogOut)} />
<Route path="/" element={()=><MongoContext.Consumer>{(mongoContext) => <Home mongoContext={mongoContext}/>}</MongoContext.Consumer>} />
</Routes>
</header>
</div>
</MongoContext.Provider>
</BrowserRouter>
);
}
export default App;
Try to wrap all your routes in the MongoContext.Consumer:
<BrowserRouter>
<Navigation user={user} />
<MongoContext.Provider
value={{ app, client, user, setClient, setUser, setApp }}
>
<MongoContext.Consumer>
{(mongoContext) => (
<div className='App'>
<header className='App-header'>
<Routes>
<Route
path='/signup'
element={
<Authentication mongoContext={mongoContext} type='create' />
}
/>
<Route
path='/signin'
element={<Authentication mongoContext={mongoContext} />}
/>
<Route
path='/logout'
element={<LogOut mongoContext={mongoContext} />}
/>
<Route path='/' element={<Home mongoContext={mongoContext} />} />
</Routes>
</header>
</div>
)}
</MongoContext.Consumer>
</MongoContext.Provider>
</BrowserRouter>;
hello
I am trying to make a menu toggle, where I have a variable with false as initial value, using react createContext and useContext hook, I set the initial state as true
// useMenu Context
import React, { useContext, useState } from 'react'
export const useToggle = (initialState) => {
const [isToggled, setToggle] = useState(initialState)
const toggle = () => setToggle((prevState) => !prevState)
// return [isToggled, toggle];
return { isToggled, setToggle, toggle }
}
const initialState = {
isMenuOpen: true,
toggle: () => {},
}
export const MenuContext = React.createContext(initialState)
const MenuProvider = ({ children }) => {
const { isToggled, setToggle, toggle } = useToggle(false)
const closeMenu = () => setToggle(false)
return (
<MenuContext.Provider
value={{
isMenuOpen: isToggled,
toggleMenu: toggle,
closeMenu,
}}>
{children}
</MenuContext.Provider>
)
}
export default MenuProvider
export const useMenu = () => {
return useContext(MenuContext)
}
so If true it will show the Menu if false it will show the Div where there a div
App.js
const { isMenuOpen } = useMenu()
//the providder
<MenuProvider>
<Header mode={theme} modeFunc={toggleTheme}/>
{isMenuOpen ? (
<Menu />
) : (
<Switch>
<Route path='/writing' component={Writings} />
<Route path='/meta' component={Meta} />
<Route path='/contact' component={Contact} />
<Route path='/project' component={Project} />
<Route exact path='/' component={Home} />
<Route path='*' component={NotFound} />
</Switch>
)}
<Footer />{' '}
</MenuProvider>
and when I add an onclick event the NavLink button of the menu to close it it does not work
Menu
const { closeMenu } = useMenu()
// return statement
{paths.map((item, i) => {
return (
<MenuItem
key={i}
link={item.location}
svg={item.icon}
path={item.name}
command={item.command}
onClick={closeMenu}
/>
)
})}
where did I go wrong
Issue
I suspect the issue is in App where you've a useMenu hook outside the MenuProvider used in App. This useMenu hook is using a MenuContext context but in the absence of a provider it instead uses the default initial context value.
const initialState = {
isMenuOpen: true,
toggle: () => {},
};
export const MenuContext = React.createContext(initialState);
export const useMenu = () => {
return useContext(MenuContext)
};
React.createContext
const MyContext = React.createContext(defaultValue);
Creates a Context object. When React renders a component that
subscribes to this Context object it will read the current context
value from the closest matching Provider above it in the tree.
The defaultValue argument is only used when a component does not
have a matching Provider above it in the tree. This default value can
be helpful for testing components in isolation without wrapping them.
Solution
Since I doubt you want to run/provide more than one menu provider I believe the solution is to move MenuProvider out of and wrap App to provide the context you are updating by nested components.
App.jsx
const { isMenuOpen } = useMenu();
...
<>
<Header mode={theme} modeFunc={toggleTheme}/>
{isMenuOpen ? (
<Menu />
) : (
<Switch>
<Route path='/writing' component={Writings} />
<Route path='/meta' component={Meta} />
<Route path='/contact' component={Contact} />
<Route path='/project' component={Project} />
<Route exact path='/' component={Home} />
<Route path='*' component={NotFound} />
</Switch>
)}
<Footer />
</>
index.jsx (?)
import App from './App.jsx';
...
//the provider
<MenuProvider>
<App />
</MenuProvider>
When changing the screen, using history.push the new screen is not loaded.
I've seen some posts about it, but no solution has actually helped.
ReactDOM.render(
<Router history={history}>
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>
</Router>,
document.getElementById('root')
);
Above is my main component, where I use redux
const isLogged = localStorage.getItem('user')
const PrivateRoute = (props) => {
return isLogged ? <Route {...props} /> : <Redirect to="/login" />
}
const App = () => {
return (
<MuiThemeProvider theme={theme}>
<div className="container-fluid p-0" style={{ backgroundColor: 'rgba(0,0,0,0.2)', }}>
<Switch>
<Route exact path='/login' component={Login} />
<PrivateRoute exact path="/" component={Dashboard} />
</Switch>
<AlertComponent />
</div>
</MuiThemeProvider>
);
}
Here is my component of routes where I have the login screen and the main screen of my application.
const authenticate = () => {
setLoading(true)
UserService.login(email, password, (response) => {
setLoading(false)
response.error ?
dispatch(createAlertError('Email e/ou senha inválida'))
:
dispatch(userSignInSuccess(response.user.shift()))
history.push('/')
})
}
Here in my login component, when calling this function the route changes, but the new component is not updated.
Here is the repository link
There are some issues in your code which you can correct
In your main component don't wrap Provider with <Router>, wrap in <Provider />, in that way route handlers can get access to the store. To know more click
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
In your App.js use <Router> and no need to inject history object there, when you're using react router, connected component will have access to it.
Like the <PrivateRoute> create one more component as <PublicRoute> which will do the exact opposite check of <PrivateRoute>
const isLogged = localStorage.getItem('user')
const PrivateRoute = (props) => {
return isLogged ? <Route {...props} /> : <Redirect to="/login" />
}
const App = () => {
return (
<MuiThemeProvider theme={theme}>
<div className="container-fluid p-0" style={{ backgroundColor: 'rgba(0,0,0,0.2)', }}>
<Router>
<Switch>
<Route exact path='/login' component={Login} />
<PrivateRoute exact path="/" component={Dashboard} />
</Switch>
</Router>
<AlertComponent />
</div>
</MuiThemeProvider>
);
}
Try above steps and let me know your progress, Thanks
const authenticate = () => {
setLoading(true)
UserService.login(email, password, (response) => {
setLoading(false)
response.error ?
dispatch(createAlertError('Email e/ou senha inválida'))
:
dispatch(userSignInSuccess(response.user.shift()), history.push('/'), history.go(0))
})
}
history.go(0) to render the page again