I get a username from "login.jsx" and i want to pass it to the "App.js" to access in everywhere. How can I do that?
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
}, []);
return (
<BrowserRouter>
<Offer/>
<Navigationbar/>
<Route path="login" element={user ? <Navigate to="/courses" /> : <Login />} />
</Routes>
<Footer />
</BrowserRouter>
);
}
you can pass callback function to Login component as props and call this prop inside Login component and pass user name as argument, then inside callback function you call setUser function to update the value of user
function App() {
const [user, setUser] = useState(null);
const updateUser = (value) => {
setUser(value);
}
useEffect(() => {
}, []);
return (
<BrowserRouter>
<Offer/>
<Navigationbar/>
<Route path="login" element={user ? <Navigate to="/courses" /> : <Login updateUser={updateUser} />} />
</Routes>
<Footer />
</BrowserRouter>
);
}
Here is How you can do that
App.jsx
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
}, []);
const updateUser = (newUser) => {
setUser(newUser);
}
return (
<BrowserRouter>
<Offer/>
<Navigationbar/>
<Route path="login" element={user ? <Navigate to="/courses" /> : <Login onNewUser={updateUser} />} />
</Routes>
<Footer />
</BrowserRouter>
);
}
In Login.jsx when you get new user call props.updateUser(user)
You can do that with React Context;
Basically, contexts are global states.
You can read more here : https://reactjs.org/docs/context.html
It allows you to wrap your <App /> into a context provider and pass to your wrapper a value that you will be able to user in your App.
Related
I'm trying to maintain state values between routes in context. But it gets reset when the route changes.
aackage.json:
"react-router-dom": "^6.8.0",
"react-dom": "^18.2.0",
"react": "^18.2.0",
App.js:
export default const App = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const getData = async () => {
setLoading(true);
const data = await axios.get("url", {
withCredentials: true,
});
setData(data);
setLoading(false);
};
useEffect(() => {
getData()
console.log("I run on route change");
}, []);
const GlobalContextValue= {
data: data,
loading: loading,
};
return (
<>
<GlobalContextProvider value={GlobalContextValue}>
<BrowserRouter>
<Routes>
<Route index element={<HomePage />} />
<Route path="/:slug" element={<PostPage />} />
{/* <Route path="*" element={<NoPage />} /> */}
</Routes>
</BrowserRouter>
</<GlobalContextProvider />
</>
)
}
Whenever I try to access any route the getData function inside the useEffect calls which inturns resets the data. I have attached a CodeSandbox to replicate the same
I don't know if this problem is related to reactJs or react-router. Thanks in advance
As you don't seem to have any navigation link, I assume you are using the browser search bar, or a normal HTML <a> tag. Well, doing so refreshes the page, so the entire app gets re-created.
Using useNavigate or Link from React Router Dom, doesn't refresh the page, hence your context data remains untouched:
const HomePage = () => {
return (
<>
<h1>Hii Homepage </h1>
<Link to="/1">Go to PostPage</Link>
</>
);
};
const PostPage = () => {
const params = useParams();
return (
<>
<h1>Hii PostPage {params.slug} </h1>
<Link to="/">Go to HomePage</Link>
</>
);
};
export default function App() {
useEffect(() => {
console.log(
"I run on load and route change with browser search bar, not with useNavigate or Link"
);
}, []);
return (
<>
{/* This context wrapping BrowserRouter keeps its value if you navigate with Link or
useNavigate. */}
<GlobalContextProvider value={{ key: "some value" }}>
<BrowserRouter>
<Routes>
<Route index element={<HomePage />} />
<Route path="/:slug" element={<PostPage />} />
{/* <Route path="*" element={<NoPage />} /> */}
</Routes>
</BrowserRouter>
</GlobalContextProvider>
</>
);
}
This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 21 days ago.
I have a problem now because it won't navigate me after I click log in, it takes cookies, but won't relocate. You can show my components look right now. Everything is fine, except when I click log in, it won't relocates me to the home page.
Components:
<App /> component
function App() {
const [userData, setUserData] = useState();
const [login, setLogin] = useState()
return (
<div className="App">
<Router>
<Routes>
<Route element={<PrivateRoutes login={login} userData=
{userData} />}>
<Route element={<Home />} path="/" exact />
<Route element={<Products />} path="/products" />
</Route>
<Route element={<Login setUserData={setUserData} setLogin=
{setLogin} />}
path="/login" />
</Routes>
</Router>
</div>
);
}
<Login /> component
const Login = ({ setUserData }) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [checking, setChecking] = useState(true)
const [loginStatus, setLoginStatus] = useState("");
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
username: username,
password: password,
}).then((response) => {
setUserData(response.data);
navigate("/");
});
};
useEffect(() => {
if (userData?.loggedIn) {
navigate("/");
}
}, [userData]);
}
return (
<div>
<div>Prijava</div>
<div>
<form onSubmit={login}>
<label>Korisničko ime</label>
<input
type="text"
onChange={(e) => {
setUsername(e.target.value);
}}
/>
<label>Lozinka</label>
<input
type="password"
onChange={(e) => {
setPassword(e.target.value);
}}
></input>
<button>Prijavi se</button>
</form>
</div>
</div>
);
};
<PrivateRoutes /> component
const PrivateRoutes = ({userData, login}) => {
return !login ? <p>Checking...</p>: userData?.loggedIn ? <Outlet
/> : <Navigate to="/login" />;
};
Console log error, after implementing code.
Newest App and PrivateRoutes version
When the page loads, userData is not set, because the useEffect runs after the JSX is rendered. Hence your condition is falling to <Navigate to="/login" /> instead of <Outlet/>.
You could use an additional state, called checking, for example, use it to display some loading message while the data is fetched.
Also, move the useEffect to check the login state on load inside PrivateRoutes. Changes your components as follows.
App:
function App() {
const [userData, setUserData] = useState();
const [checking, setChecking] = useState(true);
useEffect(() => {
Axios.get("http://localhost:3001/login")
.then((response) => {
if (response.data.loggedIn == true) {
setUserData(response.data);
}
return;
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setChecking(false);
});
}, []);
return (
<div className="App">
<Router>
<Routes>
<Route element={<PrivateRoutes userData={userData} checking={checking} />}>
<Route element={<Home />} path="/" exact />
<Route element={<Products />} path="/products" />
</Route>
<Route element={<Login setUserData={setUserData} userData={userData} />} path="/login" />
</Routes>
</Router>
</div>
);
}
PrivateRoutes:
const PrivateRoutes = ({ userData, checking }) => {
return checking ? <p>Checking...</p> : userData?.loggedIn ? <Outlet /> : <Navigate to="/login" />;
};
Login:
import { useNavigate } from "react-router-dom";
const Login = ({ setUserData, userData }) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
username: username,
password: password,
}).then((response) => {
setUserData(response.data);
});
};
useEffect(() => {
if (userData) {
navigate("/");
}
}, [userData]);
return (
<div>
<form>
<div>Prijava</div>
<div>
<form onSubmit={login}>
<label>Korisničko ime</label>
<input
type="text"
onChange={(e) => {
setUsername(e.target.value);
}}
/>
<label>Lozinka</label>
<input
type="password"
onChange={(e) => {
setPassword(e.target.value);
}}
></input>
<button>Prijavi se</button>
</form>
</div>
</form>
</div>
);
};
To resolve the issue with the userData state being undefined during the initial render, you can initialize it with a default value in the state declaration, for example:
const [userData, setUserData] = useState({ loggedIn: false });
Additionally, you can wrap the conditional rendering in a useEffect hook that depends on userData:
useEffect(() => {
if (userData?.loggedIn) {
// Render the Outlet component
} else {
// Render the Navigate component
}
}, [userData]);
I am a newbie trying to build a React blog. Routes etc work fine except when a dynamic route comes into question. The Link does not render unless refreshed.
Here is App.js where the Routes are defined
function App() {
const [username, setUsername] = useState('');
const [loggedIn, setLoggedIn] = useState(false);
return (
<>
<Router history={customHistory}>
<div className="App">
<UserContext.Provider value={{ username, setUsername, loggedIn, setLoggedIn }}>
<Navigation />
<Switch>
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
<Route exact path="/blog/:id" component={BlogDetails} />
<Route path="/content" component={Content} />
<Route path="/logout" component={Logout} />
<Route exact path="/" component={Home} />
</Switch>
</UserContext.Provider>
</div>
</Router>
</>
)
}
export default App;
Here is BlogItems.js which calls the :
import { Link } from 'react-router-dom';
function BlogItems() {
const [blogList, setBlogList] = useState([])
useEffect(() => {
Axios.get("http://localhost:3001/api/get"
, { withCredentials: true })
.then((res) => {
setBlogList(res.data);
});
}, []);
return (
<div className="blog-list">
{blogList && blogList.map((blog) => {
return (
<div className="blog-elems" key={blog && blog.id}>
<Link to={`/blog/${blog.id}`}>{blog.title}</Link>
<h6>By <span>Ritu Rawat</span> on {DateFormater(blog.dated)}</h6>
<div className="blog-list-body">{blog && blog.body.slice(0, 200) + '...'}</div>
</div>
);
})}
</div>
) }
export default BlogItems;
and this is the actual BLogDetails Page which needs to be called from the dynamic route:
const BlogDetails = (props) => {
console.log(props);
//const { username } = useContext(UserContext);
const [blog, setBlog] = useState(null);
const { id } = useParams();
useEffect(() => {
console.log("BLOG ARTICLE");
Axios.get(`http://localhost:3001/api/blog/${id}`
, { withCredentials: true })
.then((res) => {
setBlog(res.data[0]);
});
}, [id]);
return (
<>
<div className="blog-background">
<div className="blog-list">
<div className="blog-elems" key={blog && blog.id}>
<h2> {blog && blog.title}</h2>
<h6>By <span>Ritu Rawat</span> on {blog && DateFormater(blog.dated)}</h6>
<div className="blog-list-body">{blog && blog.body}</div>
</div>
<Comments id={id} />
</div>
</div>
</>
)
}
export default BlogDetails;
I'm building a small app with firebase and react and currently working on implementing the authentication. I've set the onAuthStateChanged in my app component as a side effect and whenever user is logged in it should be redirected to a desired component from ProtectedRoute.
This works correctly but unfortunately when refreshing the page the ProtectedRoute is not rendering correct component and is just firing redirection.
I get what is happening: on refresh user is empty and only after then it change so I would expect to see a screen flicker and a proper redirection.
Could you please look at below code and maybe tell me how to fix this behavior?
App component:
const App = () => {
const [authUser, setAuthUser] = useState<firebase.User | null>(null);
const Firebase = useContext(FirebaseContext);
useEffect(() => {
const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
authUser ? setAuthUser(authUser) : setAuthUser(null);
});
return () => authListener();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<AuthUserContext.Provider value={authUser}>
<Router>
<div>
<Navigation />
<hr />
<Route exact path={ROUTES.LANDING} component={Landing} />
<Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
<Route exact path={ROUTES.SIGN_IN} component={SignIn} />
<Route
exact
path={ROUTES.PASSWORD_FORGET}
component={PasswordForget}
/>
<ProtectedRoute exact path={ROUTES.HOME} component={Home} />
<ProtectedRoute exact path={ROUTES.ACCOUNT} component={Account} />
<Route exact path={ROUTES.ACCOUNT} component={Account} />
<Route exact path={ROUTES.ADMIN} component={Admin} />
</div>
</Router>
</AuthUserContext.Provider>
);
};
Protected Route:
interface Props extends RouteProps {
component?: any;
children?: any;
}
const ProtectedRoute: React.FC<Props> = ({
component: Component,
children,
...rest
}) => {
const authUser = useContext(AuthUserContext);
return (
<Route
{...rest}
render={(routeProps) =>
!!authUser ? (
Component ? (
<Component {...routeProps} />
) : (
children
)
) : (
<Redirect
to={{
pathname: ROUTES.SIGN_IN,
state: { from: routeProps.location },
}}
/>
)
}
/>
);
};
Found the fix. Had to add the flag checking for user authentication status (default value of that flag is set to true). Flag needs to be passed to ProtectedRoute as prop and if is True then render some loading component:
App component:
const App = () => {
const [authUser, setAuthUser] = useState(false);
const [authPending, setAuthPending] = useState(true);
const Firebase = useContext(FirebaseContext);
useEffect(() => {
const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
setAuthUser(!!authUser);
setAuthPending(false);
});
return () => authListener();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<AuthUserContext.Provider value={authUser}>
<Router>
<div>
<Navigation />
<hr />
<Switch>
<Route exact path={ROUTES.LANDING} component={Landing} />
<Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
<Route exact path={ROUTES.SIGN_IN} component={SignIn} />
<Route
exact
path={ROUTES.PASSWORD_FORGET}
component={PasswordForget}
/>
<ProtectedRoute
pendingAuth={authPending}
exact
path={ROUTES.HOME}
component={Home}
/>
<ProtectedRoute
pendingAuth={authPending}
exact
path={ROUTES.ACCOUNT}
component={Account}
/>
<Route exact path={ROUTES.ACCOUNT} component={Account} />
<Route exact path={ROUTES.ADMIN} component={Admin} />
</Switch>
</div>
</Router>
</AuthUserContext.Provider>
);
};
ProtectedRoute:
interface Props extends RouteProps {
component?: any;
children?: any;
pendingAuth: boolean;
}
const ProtectedRoute: React.FC<Props> = ({
component: Component,
children,
pendingAuth,
...rest
}) => {
const authUser = useContext(AuthUserContext);
if (pendingAuth) {
return <div>Authenticating</div>;
}
return (
<Route
{...rest}
render={(routeProps) =>
!!authUser ? (
Component ? (
<Component {...routeProps} />
) : (
children
)
) : (
<Redirect
to={{
pathname: ROUTES.SIGN_IN,
state: { from: routeProps.location },
}}
/>
)
}
/>
);
};
i'm trying to use React Context to manage authentication, but i can't see the value that return the context in PrivateRoute.js
App.js
render() {
return (
<>
<BrowserRouter>
<Islogin>
<Header/>
<Banner/>
<Switch>
<PrivateRoute exact path="/index" component={Landing} />
<PrivateRoute path="/upload" component={Upload} exact />
<PublicRoute restricted={false} path="/unauth" component={Unauthorized} exact />
</Switch>
</Islogin>
</BrowserRouter>
</>
);
}
}
export default App;
the console log of isAuthenticated returns undefined
PrivateRoute.js
const PrivateRoute = ({component: Component, ...rest}) => {
const isAuthenticated = useContext(AuthContext)
console.log(isAuthenticated)
const [validCredentials, setValidCredentials] = React.useState(false)
React.useEffect(() => {
if (typeof isAuthenticated === 'boolean') {
setValidCredentials(isAuthenticated)
}
}, [isAuthenticated])
return (
// Show the component only when the user is logged in
// Otherwise, redirect the user to /signin page
<Route {...rest} render={props => (
validCredentials ?
<Component {...props} />
: <Redirect to="/unauth" />
)} />
);
};
export default PrivateRoute;
IsLogin.js
The api call works and the console log shows true.
export default function Islogin({ children }) {
var [auth, setAuth] = React.useState(false)
React.useEffect(() =>{
axios.post('/api/auth').then(response => {
var res = response.data.result;
console.log("try")
console.log(res)
setAuth(res)
})
},[])
return (
<AuthContext.Provider value={auth}>
{children}
</AuthContext.Provider>
)
}
You may need to import it at the top of the file that you are using it in (PrivateRoute.js)
Try this:
import {useContext} from 'react'