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');
Related
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 setting up React-Redux to my web application, I have been getting a Could not find "store" in the context of "Connect(Register)".
I figured maybe I would have to pass the store that I created in [[2]] store.js and it worked but I haven't seen it done this way online and when I tried using redux DevTools it kept showing no store found.
I thought the purpose of connect() was to wrap around the component and access the global state in redux via mapStateToProps. If someone could point me in the right direction or possibly explain why I am getting this error would great!
Something things I have tried:
Since I was had a container component LoginContainer.js (which I could access via connect()(LoginContainer) I thought I would able to access the state here and pass down the props to register.js component and then propagate up state changes.
I have been thinking of changing register.js to a functional component because maybe this may be affecting it somehow?
I have been reading about context and how by using <Provider store={store}> at the index.js it should have allowed access to the Redux store via connect(mapState,MapDispach)(Component) however I still cannot distinguish how or when I am losing to context to Provider.
//index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux"
import { BrowserRouter, Route } from "react-router-dom";
import store from "./store";
ReactDOM.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById("root")
);
// App.js
import React, { Component } from "react";
import {
Route,
Switch
} from "react-router-dom";
import LoginContainer from "./containers/login";
class App extends Component{
render() {
return (
<div className="App">
<header className="App-header">
<Switch>
<Route path='/login' component={LoginContainer} />
</Switch>
</header>
</div>
);
}
}
export default App;
// LoginContainer.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Register from "../componets/Registration/register";
import Login from "../componets/Login/login";
import { withRouter } from "react-router-dom";
import { Input, Button } from "semantic-ui-react";
import { connect } from "react-redux";
import store from "../store";
class LoginContainer extends Component {
constructor(props) {
super(props);
this.state = {
//some local UI states
};
}
render() {
return (
<div>
<div className="ToggleContainer">
<div className={"toggle"}>
<Button.Group>
<Button
onClick={this.registerHandler}
positive={this.state.registerOn}
>
SignUp
</Button>
<Button.Or />
<Button onClick={this.loginHandler} positive={this.state.loginOn}>
Login
</Button>
</Button.Group>
</div>
<Provider store = {store}> <Register {...this.props} /> </Provider> // [[6]], uncommented [[4]] works but is it different than method [[1]]?
// <Register{...this.props} /> // [[2]] cannot connect to store
// <Register store={store} {...this.props} />} [[1]] can connect store via directly sending store //
</div>
</div>
);
}
}
// ReactDOM.render(<LoginContainer />, document.getElementById("root")); [[answer]] removing this fixed the problem
const mapStateToProps = state => {
return {
loggedIn: state.registration.isLoggedIn
};
};
const mapDisptachToProps = dispatch => ({
registerUser: id => dispatch({ type: "SIGN_IN" })
});
export default withRouter(
connect(
mapStateToProps,
mapDisptachToProps
)(LoginContainer)
);
//components/register.js
import React, { Component } from "react";
import { Input, Button } from "semantic-ui-react";
import { connect } from "react-redux";
class Register extends Component {
constructor(props) {
super(props);
// Local State
}
const check() {
let userData: User;
if (validEmail(this.state.email)) {
userData = {
//data
};
let user = await this.handleSignUp(userData);
const res = JSON.parse(user);
if (res.message === "Success") {
alert('Account success')
}
if(typeof user === 'string'){
user =JSON.parse(user)
}
} else {
this.setState({ hiddenErrorMessage: false });
}
this.props.registerUser(userData);
// func: returns true if it is valid, o.w. false
function validEmail(email) {
}
};
//handlers()
render() {
return (
// eslint-disable-next-line react/jsx-filename-extension
<div>
<Input
className="customInput"
onChange={this.handleEmail}
placeholder="Email"
/>
<Input
className="customInput"
onChange={this.handlePassword}
placeholder="Password"
type="password"
/>
<br />
<Button
size="big"
className="customButton"
onClick={this.checkUserInput}
>
Sign up
</Button>
<p>
<span hidden={this.state.hiddenErrorMessage}>
Invalid Email/Passoword
</span>
</p>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loggedIn:state.registration.isLoggedIn
}
};
const mapDisptachToProps = dispatch => ({
registerUser: id => dispatch({ type: "SIGN_IN" }) // return dispatch
});
export default Register // [[3]] Works as expected
// export default connect( [[4]] Could not find "store" in the context of "Connect(Register)". Either wrap the root component in a <Provider> Error
// mapStateToProps,
// mapDisptachToProps
// )(Register);
//store.js
import { createStore } from "redux";
import rootReducer from './reducers';
export default createStore(rootReducer);
//reducers/index.js
import { combineReducers } from "redux";
import registration from "./registration";
export default combineReducers({ registration });
//actionType.js
const SIGN_IN = 'SIGN_IN';
export { SIGN_IN };
//reducers/registration.js
const initialState = {
isAuthenticated: false,
isLoggedIn: false,
isLoggedOut: false,
userId : ''
};
export default (state = initialState, action) => {
if (action.type === "SIGN_IN") {
return { ...state, isLoggedIn: true };
}
if (action.type === "SIGN_OUT") {
return { ...state, isLoggedOut: true };
}
return state;
};
Solution to this problem was removing the next code from the LogainContainer:
ReactDOM.render(<LoginContainer />, document.getElementById("root"));
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);
I'm getting a new project going. I am using create-react-app with redux and thunk. The api server is a separate project, and uses node/express and mongo/mongoose for the database. I am trying to redirect to an item list view after either a create or delete item action. If the redirect is in the component itself, the redirect happens before the action is completed, and the refetch of the list happens before the create or delete happens. Therefore I am trying to use thunk to dispatch a redirect action in the .then() part of the action. The redirect successfully changes the url in the browser, but no re-render is triggered. How can I get the redirect to from either the create action or delete action to trigger a re-render of the stockitems list component?
full code is at: https://github.com/jhlindell/BarCode-app/tree/stockitem
server is at: https://github.com/jhlindell/BarCode-server/tree/jon
My index.js:
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import { ConnectedRouter } from 'connected-react-router'
import { createStore, applyMiddleware, compose } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import { createBrowserHistory } from 'history';
// import { browserHistory } from 'react-router'
import reducers from './reducers';
import thunk from 'redux-thunk';
import { connectRouter, routerMiddleware } from 'connected-react-router'
import { logger } from 'redux-logger';
const history = createBrowserHistory();
const reactRouterMiddleware = routerMiddleware(history);
const middleWares = [
thunk,
logger,
reactRouterMiddleware
]
const store = createStore(
connectRouter(history)(reducers),
composeWithDevTools(applyMiddleware(...middleWares)));
ReactDOM.render(
<Provider store = {store}>
<ConnectedRouter history={history}>
<App history={history}/>
</ConnectedRouter>
</Provider>
, document.getElementById('root'));
registerServiceWorker();
My app.js file:
import './App.css';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Footer from './components/Nav/Footer';
import NavBar from './components/Nav/NavBar';
import React, { Component } from 'react';
import StockItemCreate from './components/StockItems/StockItemCreate';
import StockItemDetail from './components/StockItems/StockItemDetail';
import StockItemList from './components/StockItems/StockItemList';
import HomePage from './components/HomePage';
class App extends Component {
com
render() {
const flexCol = {
display: 'flex',
flexDirection: 'column',
};
const flex0 = {
flex: 0
};
const flex1 = {
display: 'flex',
flex: '1 1 100%',
};
return (
<Router>
<div className="App" style={flexCol}>
<div style={flex0}>
<NavBar />
</div>
<div style={flex1} id="mainBlock">
<Switch>
<Route exact path='/' component={HomePage} />
<Route exact path='/stockitems/create' component={StockItemCreate} />
<Route exact path='/stockitems' component={StockItemList} />
<Route path='/stockitems/:id' component={StockItemDetail} />
</Switch>
</div>
<div style={flex0}>
<Footer />
</div>
</div>
</Router>
);
}
}
export default App;
the list component:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import React, {Component} from 'react';
import { getStockItemList, clearStockItemList } from '../../actions';
const listStyle = {
display: 'flex',
margin: 'auto'
}
class StockItemList extends Component {
componentDidMount(){
this.props.getStockItemList();
}
componentWillUnmount(){
this.props.clearStockItemList();
}
render(){
return (
<div style={listStyle}>
{this.props.stockItemList ? <ul className="list-group">
{this.props.stockItemList.map((item) => {
return <li
className="list-group-item"
key={item.name}
onClick={()=> this.props.history.push(`/stockitems/${item._id}`)}
>{item.name}</li>
})}
</ul> : <span>loading...</span>}
</div>
);
}
}
function mapStateToProps(state){
return { stockItemList: state.stockItemList }
}
function mapDispatchToProps(dispatch){
return bindActionCreators({ getStockItemList, clearStockItemList }, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(StockItemList);
the create component:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import React, {Component} from 'react';
import { createStockItem } from '../../actions'
const cardStyle = {
display: 'flex',
margin: 'auto',
};
const formStyle = {
display: 'flex',
flexDirection: 'column',
width: '80%',
margin: 'auto'
};
class StockItemCreate extends Component{
constructor(props){
super(props);
this.state = {
name: '',
description: ''
}
}
handleFormSubmit = (event) => {
event.preventDefault();
this.props.createStockItem(this.state);
this.clearForm();
//this.props.history.push('/stockitems');
}
handleInputChange = (event) => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({[name]: value});
}
clearForm = () => {
this.setState({ name: '', description: ''});
}
render(){
return(
<form className="card" onSubmit={this.handleFormSubmit} style={cardStyle}>
<div className="card-header">
<h3>Add new ingredient</h3>
</div>
<div className="card-block mt-2">
<div style={formStyle}>
<label>Name</label>
<input name="name" type="text"
onChange={(e) => {this.handleInputChange(e)}}
placeholder="Name"
value={this.state.name}/>
</div>
<div className="mt-2" style={formStyle}>
<label>Description</label>
<input name="description" type="text"
onChange={(e) => {this.handleInputChange(e)}}
placeholder="Description"
value={this.state.description}/>
</div>
</div>
<div className="btn-group mb-2 mt-2" style={{padding: '0', margin: 'auto'}}>
<button className="btn btn-primary" type="submit">
Submit
</button>
<button className="btn btn-secondary" type="button" onClick={()=>this.clearForm()}>
Cancel
</button>
</div>
</form>
)
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({ createStockItem }, dispatch);
}
export default connect(null, mapDispatchToProps)(StockItemCreate);
and finally, the action file where the redirect is called:
import axios from 'axios';
import { push, replace } from 'connected-react-router'
const URL = 'http://localhost:8000';
export function getStockItemList(){
return function(dispatch){
axios.get(`${URL}/api/stock_items/`)
.then((response) => {
dispatch({ type: 'STOCK_ITEM_LIST', payload: response.data });
})
.catch((error) => {
console.log('error getting stock items');
});
}
}
export function clearStockItemList(){
return { type: 'CLEAR_STOCK_ITEM_LIST' };
}
export function getStockItemById(id){
return function(dispatch){
axios.get(`${URL}/api/stock_items/${id}`)
.then((response) => {
dispatch({ type: 'SINGLE_STOCK_ITEM', payload: response.data });
})
.catch((error) => {
console.log('error getting stock item by id');
dispatch(push('/stockitems'));
});
}
}
export function clearSingleStockItem(){
return { type: 'CLEAR_SINGLE_STOCK_ITEM' };
}
export function createStockItem(item){
return function(dispatch){
axios.post(`${URL}/api/stock_items/`, item)
.then((response)=> {
console.log("response", response);
dispatch(push('/stockitems'));
})
.catch((error) => {
//create error container to post error to
console.log('error creating stock item', error);
});
}
}
export function deleteStockItem(id){
return function(dispatch){
axios.delete(`${URL}/api/stock_items/${id}`)
.then((response)=> {
console.log("delete response: ", response);
dispatch(push('/stockitems'));
})
.catch((error) => {
//create error container to post error to
console.log('error deleting stock item', error);
});
}
}
Consider using push from connected-react-router as an "action creator" instead of this.props.history.push.
As per this SO answer:
Changing from this:
<Switch>
<Route path="/">
<Welcome />
</Route>
<Redirect to="/" />
</Switch>
To this:
<Switch>
<>
<Route path="/">
<Welcome />
</Route>
<Redirect to="/" />
</>
</Switch>
... worked for me.
I was trying to implement a basic fallback redirect for any paths not explicitly specified, so random paths like http://localhost:3000/askfjasdf would redirect to http://localhost:3000. For some reason adding the fragment as a top level child of <Switch> did the trick.
I am new in React. As I read many documents, I realized that the state of the application should managed outside each components. And I choose Redux for my project.And I tried to pass username and password from my SigIn component. But when I click on the login button , the default statement inside the switch is always executed.The code are given below.
SignIn.jsis as below
import React from 'react';
import Header from './Head.js';
import { logIn } from '../actions/index.js';
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
class SignIn extends React.Component{
constructor(){
super();
this.logInClick = this.logInClick.bind(this);
}
logInClick() {
let { dispatch } = this.props;
const data = {username:'sojimon#gmail.com', password:'12345'}
// this.props.logIn(data);
this.props.dispatch(logIn(data));
}
render(){
return(
<div>
<Header/>
<br/>
<div className="col-md-4 col-md-offset-4">
<div className="well">
<h4 className="signin_header">Sign In</h4>
<div>
<div>
<label>Email:</label>
<input type="text" className="form-control" />
</div>
<div>
<label>Password:</label>
<input type="text" className="form-control"/>
</div>
<br/>
<button className="btn btn-primary" onClick={ this.logInClick }>Login</button>
</div>
</div>
</div>
</div>
)
}
}
const matchDispatchToProps = (dispatch) => ({
// logIn: (data) => dispatch(logIn(data)),
})
SignIn.propTypes = {
logIn: React.PropTypes.func
}
export default connect (matchDispatchToProps)(SignIn);
And my action/index.js as follows,
import * as types from './types.js';
export const logIn = (state, data) => {
return {
type: types.LOG_IN,
state
}
}
And reducers/logIn.js is,
import React from 'react';
import { LOG_IN } from '../actions/types.js'
const logIn = (state = [], action) => {
switch (action.type) {
case 'LOG_IN':
console.log('switch Case ==> LOG_IN');
return [
...state,
{
username: 'asdf',
password: '123',
}
]
// return action.logIn
default:
console.log('switch Case ==> Default');
return state
}
}
export default logIn
And created store in index.js file as,
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/App';
import './app/index.css';
import Routes from './app/route';
import { createStore } from 'redux' // import store
import { Provider } from 'react-redux' // import provider
import myApp from './app/reducers/index.js'
let store = createStore(myApp);
ReactDOM.render(
<Provider store={store}>
<Routes />
</Provider>,
document.getElementById('root')
);
export default store;