SetState with React hooks after axios post - javascript

I'm trying to figure out how to set my user state with react hooks after an axois post request. Struggling to find a good solution. I've read the react hooks documentation and almost all examples that I find are just when the page loads. I want to setState on form submission as the user logs in.
Login.js
const handleLogin = (evt) => {
//Prevent default form submission
evt.preventDefault();
const loginDetails = {
username: username,
password: password,
}
//send login request to api
axios.post('http://localhost:5000/users/login', loginDetails)
.then((res) => {
setUser(loginDetails.username);
history.push('/');
})
.catch((err) => {
console.log('incorrect password/user');
history.push('/login');
})
}
Due to the Asynchronous behaviour the state is not being set before the redirect. I have also tried to place the redirect in a useEffect, but it constantly reloads the page, even when I past in a dependency. How to I ensure that my state is being set, passed into my navbar component as a prop before the history.push('/') takes effect.
Edit
App.js
return (
<Router history={history} >
<div className={styles.app}>
<Navbar user={user}/>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route exact path="/blog" component={Blog}/>
<Route exact path="/blog/create" component={CreateBlog}/>
<Route exact path="/blog/:id" component={ViewBlog}/>
<Route path="/demo" component={Demo}/>
<Route path='/resume' component={Resume}/>
<Route
exact path="/login"
render={(props) => (
<Login loginHandler={handleLogin}
usernameHandler={onChangeUsername}
passwordHandler={onChangePassword}/>)}
/>
<Route exact path="/register" render={(props) => (
<Register usernameHandler={onChangeUsername}
passwordHandler={onChangePassword}
handleRegister={handleRegister}/>
)}/>
</Switch>
<Footer/>
</div>
</Router>
);
}
export default App;

Related

Refresh component on route only in React-Router V5

Is there a way to refresh a route when using react router v5.
I have a onClick function which navigates to a route and refetches the data, however that is only if I use history.push('/') I want to refresh the component even if I am just on the same route '/' for example I am currently doing:
is there a better way to navigate programatically in react-router?
currently this handleChange method is in the header component.
const handleChange = (event: any) => {
setValue(event.target.value);
if (history.location.pathname === "/") {
alert('Changing');
// need to refresh component here?
}
history.push('/');
};
routing is:
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Header links={items} locations={Locations} />
<div className="App">
<Switch>
<Route exact path="/" component={home} />
<Route path="/details/:id" render={(props: RouteComponentProps<any>) => <Detail {...props} />} />
<Route component={NotFound} />
</Switch>
</div>
</BrowserRouter>
<ReactQueryDevtools />
</QueryClientProvider>

Invalid Hook Call useSelector() with react-router

I have my state in Redux working for a shopping cart. Let's say I have two pages and want to pass the state from the shopping page to the cart/checkout page.
I am calling useSelector from this page but I get an error about an invalid Hook Call.
const CartPage = () => {
const selectedProducts = useSelector(
(state) => (state && state.products) || []
);
return (
<PageContainer>
This is the Cart Page
</PageContainer>);
};
export default CartPage;
And this component is rendered by react-dom-router like this
<AppContainer>
<Navigation />
<Switch>
<Route exact path="/" render={WelcomePage} />
<Route path="/cart" render={CartPage} />
<Route path="/shopping" render={ShoppingPage} />
</Switch>
</AppContainer>
I am storing the state from a component inside the page component ShoppingPage.
The useSelector hook is working only inside components outside the react-router-dom. Is there a way to get the state in one of these components?
EDIT: Somehow it worked using a callback inside the render attribute of Route
Here the fixed code
<AppContainer>
<Navigation />
<Switch>
<Route exact path="/" render={() => <WelcomePage /> } />
<Route path="/cart" render={() => <CartPage /> } />
<Route path="/shopping" render={() => <ShoppingPage /> } />
</Switch>
</AppContainer>

React nested route page not rendering properly as expected

I created this react component which has some nested routes. The problem is that the nested page component is either not rendering at all despite the URL changes or just returns a blank page.
I tried the suggestions from other posts like adding/removing exact in the parent route, but it's still not working.
Below are my codes:
// the parent component
<div className="App">
<Router>
<Navbar />
<Switch>
<Route exact path="/" render={() => <MyPage accessToken={accessToken} />}/>
<Route path="/editing/:playlistId" render={(props) =>
<EditingPlaylist {...props} accessToken={accessToken} />} />
</Switch>
</Router>
</div>
//the child component EditingPlaylist
render() {
const { pathname } = this.props.location;
return (
<div className="editing">
<Switch>
<Route exact path={pathname} render={() =>
<Searchbar accessToken={this.props.accessToken} />} />
<Route path={`${pathname}/test`} component={<p>Test</p>} />
<Route path={`${pathname}/album/:albumId`} render={
(props) => <AlbumPage {...props} accessToken={this.state.accessToken} />} />
<Route path={`${pathname}/artist/:artistId`} render={
(props) => <ArtistProfile {...props} accessToken={this.state.accessToken} />} />
</Switch>
</div>)
}
export default withRouter(EditingPlaylist);
Use url and path:
const { url, path } = this.props.match
to defined nested routes.
So, in nested routes:
<Switch>
<Route
exact
path={path} // HERE
render={() => <Searchbar accessToken={this.props.accessToken} />}
/>
...
</Switch>
There is a difference between url and path:
url: It is what is visible in the browser. e.g. /editing/123. This should be used in when redirecting via Link or history.push
path: It is what matched by the Route. e.g. /editing/:playlistId. This should be used when defining (nested) paths using Route.
From docs:
path - (string) The path pattern used to match. Useful for building nested <Route>s
url - (string) The matched portion of the URL. Useful for building nested <Link>s

Where can I put auto-authentication code in ReactJS?

I am using ReactJS, redux, and react-router. I am using localStorage to log in users automatically. My App.js looks like this:
` <Router>
<Provider store={store}>
<div className="App">
<Route path="/" component={NavBar} />
<Route exact path="/" component={Home} />
<Route exact path="/list" component={RoadmapList} />
<Route path="/roadmap" component={OneRoadmap} />
<Route path="/search" component={Search} />
<Route exact path="/add" component={RoadmapAdder} />
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Route
exact
path="/savedRoadmaps"
component={SavedRoadmapsView}
/>
<Route path="/profile" component={AuthorProfile} />
<Route exact path="/account/edit" component={Profile} />
</div>
</Provider>
</Router>`
I previously did auto-authentication in my NavBar component since NavBar will be in every single URL because of the <Route path="/" component={NavBar}/>, I thought I could've just used NavBar to auto-login my users using a redux action called loginUser and then update a few redux states like isAuthenticated and user_info. The problem is that if I were to render NavBar and let's say SavedRoadmapsView, SavedRoadmapsView wouldn't get the redux states immediately since it runs parallel with NavBar.
I have tried to login Users automatically in App.js, like:
` async componentDidMount() {
var email = localStorage.getItem("email");
var password = localStorage.getItem("password");
var userData = { email: email, password: password };
await loginUser(userData);
}`
It didn't seemed to work. Could it be that loginUser is a redux action and can't be used outside of Provider? What are your insights on this? Thanks in advance.

Render Same Component With Multiple Paths React Router Dom

I was looking for the simplest way to render the same component but from different paths.
I have the following so that both "/" and "/login" render the Login component.
import React from "react";
import { Route, Switch, Redirect } from 'react-router-dom';
import './App.scss';
import Login from "../../login/Login";
const App = () => {
return (
<div className="App">
<div className="App-container">
<Switch>
<Route exact path={["/", "/login"]} component={() =>
<Login login={true} />}/>
<Redirect to="/" />
</Switch>
</div>
</div>
);
}
export default App;
This does appear to work, however, it returns an error in the console.
Warning: Failed prop type: Invalid prop 'path' of type 'array' supplied to 'Route', expected 'string'.
I'm trying to do this...
<Route exact path={"/"} component={() => <Login login={true} />}/>
<Route exact path={"/login"} component={() => <Login login={true} />}/>
But with a shorter method, is this possible with react-router-dom? Any help would be greatly appreciated
You could create an array that contains the paths / and /login and use map on that array to render the same thing for both paths.
<Switch>
{["/", "/login"].map(path => (
<Route
key={path}
exact
path={path}
render={() => <Login login={true} />}
/>
))}
<Redirect to="/" />
</Switch>
If you wish to render the same component on the several routes, you can do this by specifying your path as a regular expression
lets say you want to display 'Home' component for 'Home', 'User' and 'Contact' components then here is code.
<Route path="/(home|users|contact)/" component={Home} />

Categories

Resources