I am using react with redux to build login authentication system with backend passport jwt .
login works fine before , i have added PrivateRoute to some Routes that requires authentication.
Errors i got :
src/actions/authActions.js
import { GET_ERRORS,CLEAR_ERRORS,SET_CURRENT_USER,LOGOUT_USER} from './types';
import axios from 'axios';
import setAuthToken from '../utils/setAuthToken';
import jwt_decode from 'jwt-decode';
export const loginUser= userdata =>dispatch=>{
axios.post('/api/auth/login',userdata)
.then(res=>{
console.log('loginUser action response ==>',res.data);
const {token}=res.data;
localStorage.setItem('jwtToken',token);
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
dispatch(setCurrentUser(decoded));
}).catch(err=>{
dispatch({type:GET_ERRORS,payload:err.response.data});
})
}
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
src/reducers/authReducers.js
import isEmpty from '../validation/is-empty';
import { SET_CURRENT_USER,LOGIN_USER,LOGOUT_USER} from '../actions/types';
const initialState = {
isAuthenticated: false,
user: {}
};
export default function(state = initialState, action) {
switch (action.type) {
case LOGIN_USER:
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
};
case LOGOUT_USER:
return {
...state,
isAuthenticated:false,
user: {}
};
default:
return state;
}
}
App.js
import React, { Component } from 'react';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Provider} from 'react-redux';
import store from './store';
import Footer from './partials/footer';
import Header from './partials/header';
import Login from './components/auth/login';
import { setCurrentUser ,logoutUser} from './actions/authActions';
import jwt_decode from 'jwt-decode';
import setAuthToken from './utils/setAuthToken';
import PrivateRoute from './utils/PrivateRoute';
import Dashboard from './components/user/dashboard';
import NotFound404 from './components/error/404';
if(localStorage.jwtToken){
setAuthToken(localStorage.jwtToken);
// Decode token and get user info and exp
const decoded = jwt_decode(localStorage.jwtToken);
store.dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Clear current Profile
//store.dispatch(clearCurrentProfile());
// Redirect to login
window.location.href = '/login';
}
}
export default class App extends Component {
constructor(){
super();
this.state={
isAuthenticated:store.getState().auth.isAuthenticated
}
}
render() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Header/>
<div className="container">
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/login" component={Login} />
<PrivateRoute isAuthenticated={this.state.isAuthenticated} exact path="/dashboard" component={Dashboard}/>
<Route component={NotFound404} />
</Switch>
</div>
<Footer/>
</div>
</Router>
</Provider>
);
}
}
src/components/login.js
import React, { Component } from 'react'
import { Link } from 'react-router-dom';
import classnames from 'classnames';
import { connect } from 'react-redux';
import { loginUser } from '../../actions/authActions';
import { PropTypes } from 'prop-types';
class Login extends Component {
constructor(){
super();
this.state={
email:'',
password:'',
errors:{}
}
this.handleChange=this.handleChange.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleChange(event){
this.setState({
[event.target.name]:event.target.value
});
}
handleSubmit(event){
event.preventDefault();
const user={
email:this.state.email,
password:this.state.password
}
this.props.loginUser(user);
}
componentDidMount() {
if (this.props.auth.isAuthenticated) {
this.props.history.push('/dashboard');
}
}
componentWillReceiveProps(nextProps){
if(nextProps.errors){
this.setState({
errors:nextProps.errors
});
}
if(nextProps.auth.isAuthenticated){
this.props.history.push('/dashboard');
}
}
render () {
const {errors} = this.state;
return (
<div className="row my-5">
<div className="col-md-4 offset-md-4 col-sm-12">
<div className="card shadow-sm">
<h5 className="card-header">Login</h5>
<div className="card-body">
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="email" className="label">Email</label>
<input type="email" id="email" name="email" value={this.state.email} onChange={this.handleChange} className={classnames('form-control',{'is-invalid':errors.email})}/>
{errors.email && (<div className="invalid-feedback">{errors.email}</div>)}
</div>
<div className="form-group">
<label htmlFor="password" className="label">Password</label>
<input type="password" id="password" name="password" value={this.state.password} onChange={this.handleChange} className={classnames('form-control',{'is-invalid':errors.password})}/>
{errors.password && (<div className="invalid-feedback">{errors.password}</div>)}
</div>
<button type="submit" className="btn btn-success btn-block">Login</button>
</form>
<div className="py-3 border-bottom"></div>
<Link to="/register" className="btn btn-default btn-block my-2">Haven't created account yet ?</Link>
<Link to="/forgotpassword" className="btn btn-default btn-block">Forgot Password ?</Link>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state, ownProps) => ({
auth:state.auth,
errors:state.errors
})
const mapDispatchToProps = {
loginUser
}
Login.propTypes={
auth:PropTypes.object.isRequired,
errors:PropTypes.object.isRequired,
loginUser:PropTypes.func.isRequired
}
export default connect(mapStateToProps,mapDispatchToProps)(Login)
PrivateRoute.js component
import React from 'react';
import {Route,Redirect} from 'react-router-dom';
const PrivateRoute=({component: Component, isAuthenticated, ...rest}) => {
return (
<Route
{...rest}
render={(props) => isAuthenticated === true
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
/>
)
}
export default PrivateRoute;
Please help me to solve this error .
I have solved my error by replacing PrivateRoute component as below :
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
const PrivateRoute = ({ component: Component,auth, ...rest }) => (
<Route
{...rest}
render={props =>
auth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);
PrivateRoute.propTypes = {
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(mapStateToProps)(PrivateRoute);
I suggest you use another state variable for saving request status. like loggingIn if it's true, show loading if it's false and isAuthenticated is false, the user is not requested for login and it's not logged in. so redirect it to /login.
PrivateRoute.js component
import React from 'react';
import {Route,Redirect} from 'react-router-dom';
class PrivateRoute extends Component {
render() {
const {
component: Component, loggingIn, isAuthenticated, ...rest
} = this.props;
if (loggingIn) {
return (
<div>
Please wait.
</div>
);
}
return (<Route {...rest} render={props => (isAuthenticated ? (<Component {...props} />) : (<Redirect to={{ pathname: '/login', state: { from: props.location } }} />))} />);
}}
export default PrivateRoute;
src/actions/authActions.js
import { GET_ERRORS,CLEAR_ERRORS,SET_CURRENT_USER,LOGOUT_USER} from './types';
import axios from 'axios';
import setAuthToken from '../utils/setAuthToken';
import jwt_decode from 'jwt-decode';
export const loginUser= userdata =>dispatch=>{
dispatch(loggingIn(true));
axios.post('/api/auth/login',userdata)
.then(res=>{
dispatch(loggingIn(false));
console.log('loginUser action response ==>',res.data);
const {token}=res.data;
localStorage.setItem('jwtToken',token);
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
dispatch(setCurrentUser(decoded));
}).catch(err=>{
dispatch(loggingIn(false));
dispatch({type:GET_ERRORS,payload:err.response.data});
})
}
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
export const loggingIn = status => {
return {
type: 'LOGGINGIN',
status,
}
}
src/reducers/authReducers.js
import isEmpty from '../validation/is-empty';
import { SET_CURRENT_USER,LOGIN_USER,LOGOUT_USER} from '../actions/types';
const initialState = {
isAuthenticated: false,
user: {}
};
export default function(state = initialState, action) {
switch (action.type) {
case LOGIN_USER:
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
};
case LOGOUT_USER:
return {
...state,
isAuthenticated:false,
user: {}
};
case 'LOGGINGIN':
return {
...state,
loggingIn: action.status,
};
default:
return state;
}
}
and remember pass loggingIn as props to privateRoute
Edit : show how to use in App.js
App.js
import React, { Component } from 'react';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {connect} from 'react-redux';
import Footer from './partials/footer';
import Header from './partials/header';
import Login from './components/auth/login';
import { setCurrentUser ,logoutUser} from './actions/authActions';
import jwt_decode from 'jwt-decode';
import setAuthToken from './utils/setAuthToken';
import PrivateRoute from './utils/PrivateRoute';
import Dashboard from './components/user/dashboard';
import NotFound404 from './components/error/404';
class App extends Component {
constructor(props){
super(props);
const { dispatch } = props;
if(localStorage.jwtToken){
setAuthToken(localStorage.jwtToken);
// Decode token and get user info and exp
const decoded = jwt_decode(localStorage.jwtToken);
dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
// Logout user
dispatch(logoutUser());
// Clear current Profile
//dispatch(clearCurrentProfile());
// Redirect to login
window.location.href = '/login';
}
}
}
render() {
const { isAuthenticated, loggingIn } = this.props;
return (
<Provider store={store}>
<Router>
<div className="App">
<Header/>
<div className="container">
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/login" component={Login} />
<PrivateRoute loggingIn={loggingIn} isAuthenticated={isAuthenticated} exact path="/dashboard" component={Dashboard}/>
<Route component={NotFound404} />
</Switch>
</div>
<Footer/>
</div>
</Router>
</Provider>
);
}
}
const mapStateToProps = state = {
const { loggingIn, isAuthenticated } = state.auth;
return { loggingIn, isAuthenticated }
}
export default connect(mapStateToProps)(App);
Related
Firstly, thank you to anyone who is reading this and is willing to help!
I'm trying to build a react-redux web app, and I'm having trouble accessing an id from the url in a container. The url looks like this: websitename.com/games/:game_id
I need to access that :game_id so that I can use it in a redux action to make a call to my api, but I can't figure out how to access the usage of match. I get the following error when I try to compile:
./src/containers/GameDetails.js
Line 9:19: 'match' is not defined no-undef
My app is set up with the following structure: Main.js houses the routes:
import React from "react";
import {Switch, Route, withRouter, Redirect} from "react-router-dom";
import {connect} from "react-redux";
import Homepage from "../components/Homepage";
import AuthForm from "../components/AuthForm";
import {authUser} from "../store/actions/auth";
import {removeError} from "../store/actions/errors"
import withAuth from "../hocs/withAuth";
import GameForm from "./GameForm";
import GamePage from "../components/GamePage";
const Main = props => {
const {authUser, errors, removeError, currentUser} = props;
return (
<div className="container">
<Switch>
<Route path="/" exact render={props => <Homepage currentUser={currentUser} {...props} /> } />
<Route
path="/signin" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
buttonText="Log in"
heading="Welcome Back."
{...props}
/>
)
}} />
<Route
path="/signup" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
signUp
buttonText="Sign me up"
heading="Join Weekly Matchup today."
{...props}
/>
)
}}
/>
<Route
path="/games/new" exact
component={withAuth(GameForm)}
/>
<Route
path="/games/:game_id"
render={props => {
return(
<GamePage
currentUser={currentUser}
{...props}
/>
)
}}
/>
<Redirect to="/" />
</Switch>
</div>
)
}
function mapStateToProps(state){
return {
currentUser: state.currentUser,
errors: state.errors
};
}
export default withRouter(connect(mapStateToProps, {authUser, removeError})(Main));
GamePage.js is a component that has the GameDetails container in it:
import React from "react";
import { Link } from "react-router-dom";
import GameDetails from "../containers/GameDetails";
const GamePage = ({ currentUser }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>You must sign in or sign up in order to vote for matchups and view comments.</h1>
</div>
);
}
return (
<div>
<div className="home-hero">
<GameDetails />
</div>
</div>
)
}
export default GamePage;
And GameDetails.js is where I'm trying to get the id from the url to use in my redux action:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { getGameDetails } from "../store/actions/games";
class GameDetails extends Component {
componentDidMount() {
const game_id = match.params.game_id;
this.props.getGameDetails(game_id);
}
render() {
const { match, game } = this.props;
return (
<div className="home-hero">
<div className="offset-1 col-sm-10">
<h4>You are viewing the Game Page for game.title</h4>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
game: state.game
};
}
export default connect(mapStateToProps, { getGameDetails })(
GameDetails
);
I'm still very new to react and redux, so I appreciate any help you can offer.
Thank you for your time and patience!
Try something like this in GameDetails.js and GamePage.js
import React from "react";
import { Link } from "react-router-dom";
import GameDetails from "../containers/GameDetails";
const GamePage = ({ currentUser, ...props }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>You must sign in or sign up in order to vote for matchups and view comments.</h1>
</div>
);
}
return (
<div>
<div className="home-hero">
<GameDetails {...props} />
</div>
</div>
)
}
export default GamePage;
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { getGameDetails } from "../store/actions/games";
class GameDetails extends Component {
componentDidMount() {
const {game_id}= this.props.match.params.game_id;
this.props.getGameDetails(game_id);
}
render() {
const { match, game } = this.props;
return (
<div className="home-hero">
<div className="offset-1 col-sm-10">
<h4>You are viewing the Game Page for game.title</h4>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
game: state.game
};
}
export default connect(mapStateToProps, { getGameDetails })(
GameDetails
);
I suppose you use you exported component same thing like
<Route path="/games/:game_id" component={GameDetails} />
mapStateToProps get 2 arguments state, and ownProps.
function mapStateToProps(state, ownProps) {
const { match: { params: { game_id } } } = ownProps; // you able to use game_id inside mapStateToProps
return ({ game: state.game });
}
1 solution
import React from "react";
import { Link } from "react-router-dom";
import GameDetails from "../containers/GameDetails";
const GamePage = ({ currentUser, ...routeProps }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>You must sign in or sign up in order to vote for matchups and view comments.</h1>
</div>
);
}
return (
<div>
<div className="home-hero">
<GameDetails {...routeProps} />
</div>
</div>
)
}
export default GamePage;
2 solution
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link, withRouter } from "react-router-dom";
import { getGameDetails } from "../store/actions/games";
class GameDetails extends Component {
componentDidMount() {
const game_id = match.params.game_id;
this.props.getGameDetails(game_id);
}
render() {
const { match, game } = this.props;
return (
<div className="home-hero">
<div className="offset-1 col-sm-10">
<h4>You are viewing the Game Page for game.title</h4>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
game: state.game
};
}
export default withRouter(
connect(mapStateToProps, { getGameDetails })(GameDetails)
);
Currently writing application in React + Redux stack. During development, I hit the problem where in one's of React component, Redux function work just fine and in index.js throwing that Redux function is undefined.
What I wish to do, is to keep user sign in between reloading of web page. So where pages reload, componentDidMonut function in index.js check if there is a token in localstorage then send one api call to check if JWT token did not expired. And if everthing is correct call redux function signIn() to rerender app.
In Login component login working and redirect to after sign in, unfortunately in index.js React throwing error:
.
After checking in console this function is simply undefined, and I don't know why.
Looking forward for pointing mistakes I made. Thanks in advance
Below code:
actions/index.js:
export const signIn = () => {
return {
type: "SIGN_IN"
};
};
export const signOut = () => {
return {
type: "SIGN_OUT"
};
};
reducers/authReducers:
const INITIAL_STATE = {
isSignedIn: null
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case "SIGN_IN":
return { ...state, isSignedIn: true };
case "SIGN_OUT":
return { ...state, isSignedIn: false };
default:
return state;
}
};
Login.js:
import React from "react";
import {
Button,
Container,
Form,
Grid,
Header,
Message,
Segment
} from "semantic-ui-react";
import { connect } from "react-redux";
import { signIn, signOut } from "../actions";
import api from "../api/api";
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
isSignedIn: null,
email: "",
password: "",
errorMessage: ""
};
this.errorMessageRef = React.createRef();
}
handleLogin = async () => {
try {
const response = await api.login(this.state.email, this.state.password);
if (response.status === 200) {
localStorage.setItem("token", response.data.token);
this.props.signIn();
this.props.history.push("/flat");
}
} catch (error) {
this.errorMessageRef.current.hidden = false;
this.setState({ ...this.state, errorMessage: `${error}` });
this.props.signOut();
}
};
render() {
return (
<Container>
<Grid centered columns={2}>
<Grid.Column>
<Header as="h2" textAlign="center">
Login
</Header>
<Segment>
<Form size="large">
<Form.Input
fluid
icon="user"
iconPosition="left"
placeholder="Email address"
onChange={event =>
this.setState({ email: event.target.value })
}
/>
<Form.Input
fluid
icon="lock"
iconPosition="left"
placeholder="Password"
type="password"
onChange={event =>
this.setState({ password: event.target.value })
}
/>
<Button
color="blue"
fluid
size="large"
onClick={this.handleLogin}
>
Login
</Button>
</Form>
</Segment>
<Message ref={this.errorMessageRef} negative={true} hidden={true}>
Email or password and incorrect
</Message>
<Message>
Not registered yet? Sign Up
</Message>
</Grid.Column>
</Grid>
</Container>
);
}
}
export default connect(null, { signIn, signOut })(Login);
index.js:
import React from "react";
import { createStore } from "redux";
import ReactDOM from "react-dom";
import { connect, Provider } from "react-redux";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { Container } from "semantic-ui-react";
import reducers from "./reducers";
import { signIn, signOut } from "./actions";
import api from "./api/api";
import Login from "./components/Login";
import App from "./components/pages/App.js";
import Flat from "./components/pages/Flat";
import NavBar from "./components/NavBar";
import Duties from "./components/pages/Duties";
import MyDuties from "./components/pages/MyDuties";
import CreateFlat from "./components/flat/CreateFlat";
import JoinFlat from "./components/flat/JoinFlat";
import Payments from "./components/pages/Payments";
import "./components/index.css";
const store = createStore(reducers);
class Routing extends React.Component {
constructor(props) {
super(props);
this.state = { isSignedIn: null };
}
componentDidMount() {
this.isLoggedIn();
}
isLoggedIn = async () => {
try {
if (localStorage.getItem("token") != null) throw "user not sign in";
await api.getCurrentFlatmate();
this.props.signIn();
} catch (e) {
this.props.signOut();
}
};
render() {
return (
<Router>
<NavBar />
<Switch>
<Route exact path="/" component={App} />
<Route path="/home" component={App} />
<Route path="/sign-in" component={Login} />
<Route path="/sign-out" component={App} />
<Route path="/user" component={Login} />
<Route path="/flat" component={Flat} />
<Route path="/join-flat" component={JoinFlat} />
<Route path="/create-flat" component={CreateFlat} />
<Route path="/duties" component={Duties} />
<Route path="/my-duties" component={MyDuties} />
<Route path="/payments" component={Payments} />
</Switch>
</Router>
);
}
}
export default connect(null, { signIn, signOut })(Routing);
ReactDOM.render(
<Provider store={store}>
<Routing />
</Provider>,
document.getElementById("root")
);
Try using this.props.dispatch(signOut()) instead of this.props.signOut().
And in the same way for signIn()
Hello I have a problem redirecting to a page doing a verification on a privaterouter
Unhandled Rejection (TypeError): Cannot read property 'push' of
undefined
on this line:
this.props.history.push ("/ home");
my component:
import React, { Component } from 'react';
import api from '../services/api';
import { withRouter } from 'react-router';
class LoginForm extends Component {
constructor(props){
super(props);
this.state = {
login:'',
password:'',
};
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
async onSubmit(e){
e.preventDefault();
const {login, password } = this.state;
const response = await api.post('/login', { login,password });
const user = response.data.user.login;
const {jwt} = response.data;
localStorage.setItem('token', jwt);
localStorage.setItem('user', user);
this.props.history.push("/home");
}
onChange(e){
this.setState({[e.target.name]: e.target.value});
}
render() {
const { errors, login, password, isLoading } = this.state;
return (
<form onSubmit={this.onSubmit}>
<label htmlFor="login">Login</label>
<input type="text" name="login" id="login" value={login} onChange={(e) => this.onChange(e)} placeholder="Informe seu login" />
<label htmlFor="password">Senha</label>
<input type="password" name="password" id="password" value={password} onChange={(e) => this.onChange(e)} placeholder="Informe sua senha"/>
<button className="btnEnt" type="submit">Entrar</button>
</form>
)
}
}
export default withRouter (LoginForm);
my router:
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Login from './pages/login/index';
import DashBoard from './pages/dashboard/index';
import PrivateRoute from './auth';
export default function Routes(){
return(
<BrowserRouter>
<div>
<Switch>
<Route path="/" exact component = {Login}/>
<PrivateRoute path="/home" component = {DashBoard}/>
</Switch>
</div>
</BrowserRouter>
);
}
my private route or auth router:
import React from 'react';
import { Route, Redirect} from 'react-router-dom';
const isAuth = () => {
console.log('a');
if(localStorage.getItem('token') !== null) {
console.log('true')
return true;
}
return false;
};
const PrivateRoute = ({component: Component, ...rest}) => {
return (
<Route
{...rest}
render={props =>
isAuth() ? (
<Component {...props} />
): (
<Redirect
to={{
pathname: '/',
state: {message: 'Usuário não autorizado'}
}}
/>
)}
/>
);
}
export default PrivateRoute;
I basically have my router and I also check if the user is allowed to enter this page, but I'm having trouble making it work.
Well, I read your code and here is my answer
You just need import withRouter from react-router-dom and not from react-router ;)
import { withRouter } from "react-router-dom";
And use it like
export default withRouter(LoginForm);
I am creating my first react project, i am using GitHub api to fetch user and display them firstly in card view then on clicking on more button to any profile i want to create a modal using portals in react till now i am able to create an modal but now i am not getting how to get data to that modal coponent
Here is my App.js
import React, { Fragment, Component } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Users from './components/users/Users';
import User from './components/users/User';
import Modal from './components/Modal/Modal'
import Search from './components/users/Search';
import Alert from './components/layout/Alert';
import About from './components/pages/About';
import axios from 'axios';
import './App.css';
class App extends Component {
state = {
users: [],
user: {},
loading: false,
alert: null,
modal: {},
}
// get users from Search.js
searchUsers = async text => {
this.setState({ loading: true })
const res = await axios.get(
`https://api.github.com/search/users?q=${text}&client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID
}&client_secret=${process.env.REACT_APP_GITHUB_CLIENT_SECRET}`);
this.setState({ users: res.data.items, loading: false })
console.log(text);
}
//get single profile
getUser = async username => {
this.setState({ loading: true })
const res = await axios.get(
`https://api.github.com/users/${username}?client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID
}&client_secret=${process.env.REACT_APP_GITHUB_CLIENT_SECRET}`);
this.setState({ user: res.data, loading: false });
this.setState({ modal: res.data, loadading: false });
}
//clear search
clearUsers = () => this.setState({ users: [], loading: false });
setAlert = (msg, type) => {
this.setState({ alert: { msg: msg, type: type } });
setTimeout(() => this.setState({ alert: null }), 5000);
};
render() {
return (
<Router>
<div className='App'>
<Navbar />
<div className="container">
<Alert alert={this.state.alert} />
<Switch>
<Route exact path='/'
render={props => (
<Fragment>
<Search
searchUsers={this.searchUsers}
clearUsers={this.clearUsers}
showClear={this.state.users.length > 0 ? true : false}
setAlert={this.setAlert}
/>
<Users loading={this.state.loading} users={this.state.users} />
</Fragment>
)} />
<Route path='/about' component={About} />
<Route path='/user/:login' render={props => (
<User {...props} getUser={this.getUser} user={this.state.user} loading={this.state.loading} />
)} />
<Route path='/modal/:login' render={props => (
<Modal {...props} getUser={this.getUser} modal={this.state.modal} loading={this.state.loading} />
)} />
</Switch>
</div>
</div>
</Router>
);
}
}
export default App;
here is my Modal.js
import React, { Fragment, Component } from 'react';
import ReactDom from 'react-dom';
import Spinner from '../layout/Spinner';
import { Link } from 'react-router-dom';
const modalRoot = document.getElementById('modal');
export default class Modal extends Component {
constructor() {
super();
this.el = document.createElement('div');
}
componentDidMount = () => {
modalRoot.appendChild(this.el);
};
componentWillUnmount = () => {
modalRoot.removeChild(this.el);
};
render() {
const {
children,
name,
avatar_url,
location,
bio,
blog,
followers,
following,
public_repos,
} = this.props.modal;
const { loading } = this.props;
if (loading) return <Spinner />
return (
ReactDom.createPortal(children, this.el)
)
}
}
any guide would be appriciated thanks in advance
You are passing the props already to Modal.
In Modal, do something like
Class Modal extends Component {
constructor(){
super(props);
}
render(){
const {
modal,
getUser,
loading,
anyOtherPropYouPassIn
} = this.props;
const { loading } = this.props;
if (loading) return <Spinner />
return (
ReactDom.createPortal(children, this.el)
)
}
In my app, I have a state that save userData if the user is logged in, but and I want to redirect him to /dashboard/:id with it id. But when the action is completed, the url is good (e.g : /dashboard/12) but the view is style on /login component..
router version :
"react-router": "^3.2.0",
"react-router-dom": "^4.2.2"
Here is the code :
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Login from './components/Login/Login';
import Register from './components/Register/Register';
import Dashboard from './components/Dashboard/Dashboard';
import './App.css';
class App extends Component {
render() {
return (
<Router>
<div>
<Route path="/register" component={Register} />
<Route path="/login" component={Login} />
<Route path="/dashboard/:id" component={Dashboard} />
</div>
</Router>
);
}
}
export default App;
sessionReducer:
import * as types from '../actions/types';
import initialState from './initialState';
export default function sessionReducer(state = initialState, action) {
switch(action.type) {
case types.LOG_IN_SUCCESS:
console.log(action);
return {
...state,
userData: action.payload
}
case types.LOG_IN_FAILED:
console.log('login failed');
break;
default:
return state;
}
}
authAction.js :
import { browserHistory } from 'react-router';
import { Redirect } from 'react-router-dom'
import * as types from './types';
import sessionApi from '../api/SessionApi';
import history from '../history';
export function loginSuccess(userData) {
return {
type: types.LOG_IN_SUCCESS,
payload: userData
}
}
export function loginFailed() {
return {
type: types.LOG_IN_FAILED
}
}
export function logInUser(credentials) {
return function(dispatch) {
return sessionApi.login(credentials)
.then(response => {
console.log(response);
if(response.data) {
sessionStorage.setItem('jwt', response.data.authentication_token);
dispatch(loginSuccess(response.data));
history.push('/dashboard/' + response.data.id);
} else {
dispatch(loginFailed());
}
})
.catch(error => {
throw(error);
})
}
}
The dashboard.js :
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { connect } from 'react-redux';
class Dashboard extends Component {
constructor(props) {
super(props);
console.log(props);
}
componentWillReceiveProps() {
}
render() {
console.log(this.props.userData);
return(
<div>
<h1> Hello user </h1>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
userData: state.sessionReducer.userData
}
}
export default withRouter(connect(mapStateToProps)(Dashboard));
And the loginForm.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { withRouter } from 'react-router';
import { connect } from 'react-redux';
import * as authActions from '../../actions/authActions';
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
errors: {},
isLoading: false,
};
}
onChange(e) {
this.setState({
[e.target.name]: e.target.value
})
}
onSubmit(e) {
e.preventDefault();
this.setState({ errors: {}, isLoading: true });
this.props.actions.logInUser( { data: { user: { email: this.state.email, password: this.state.password }}})
}
render() {
return(
<div>
<form onSubmit={this.onSubmit.bind(this)}>
<div className="field">
<label className="label"> Email </label>
<div className="control">
<input type="email"
name="email"
value={this.state.email}
onChange={this.onChange.bind(this)}
className="input" />
</div>
</div>
<div className="field">
<label className="label"> Mot de passe </label>
<div className="control">
<input type="password"
ref="password"
name="password"
value={this.state.password}
onChange={this.onChange.bind(this)}
className="input" />
</div>
</div>
<div className="form-group">
<input type="submit" value="Signup" className="button is-primary" />
</div>
<Link to={{ pathname: '/register' }}>Inscription</Link>
</form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(authActions, dispatch)
};
}
export default withRouter(connect(null, mapDispatchToProps)(LoginForm));
I just want to render the good component with the right view, does someone know why the Dashboard component doesn't render ?
EDIT
add index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { routerMiddleware } from 'react-router-redux'
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from './reducer/rootReducer';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
/*
* CSS
*/
import './bulma.css';
import './index.css';
/*
* Render
*/
const store = createStore(
rootReducer,
compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
registerServiceWorker();
you must to use in router v4:
<BrowserRouter>
<Switch>
<Route exact path="/testcolors" component={TestColors} />
</Switch>
</BrowserRouter>
this.props.history.push('/customers');