Control re-rendering or whole app when a store updates - javascript

Here is my App.js component where i use a provider that wrap the whole app and render on any update in my store state
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './redux/index';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Provider store={store}>
<Routes>
<Route path="/*" element={<App />} />
</Routes>
</Provider>
</BrowserRouter>
);
reportWebVitals();
and here is my action
export const ProductListAction = ({endpoint, key='', method, data, params, isload, message=false}) =>{
return async (dispatch) =>{
try {
if(isload){
dispatch({ type: 'ISLOADING', payload: {loading:true, key} });
}
await endpoint && fetchrequest({isload, endpoint: endpoint, method, data, params}).then((response) => {
if(response.data.success===true){
isload&& dispatch({ type: "SUCCESS", payload: response.data.message, message});
}
else{
isload&& dispatch({ type: "ONERROR", payload: response.data.message, message});
}
dispatch({ type: key, payload: response || {} });
}).catch((err) => {
isload&& dispatch({ type: "ONERROR", payload: err?.response?.data?.message ,message});
})
} catch (error) {
isload&& dispatch({ type: "ONERROR", payload: error?.response?.data?.message});
}
}
};
now the problem is that when my whole app renders the times that how many API calls I want my current app renders only one time regarding how many api calls.

Related

React Redux Class Component TypeError: Cannot read properties of undefined (reading 'params')

I get the error TypeError: Cannot read properties of undefined (reading 'params')
so I try to get data detail users using id, and when I try to see, I get the error
app.js
import React, { Component } from 'react'
import Jumbotron from './component/Jumbotron'
import NavbarComponent from './component/NavbarComponent'
import {
BrowserRouter,
Route,
Routes,
} from "react-router-dom";
import HomeContainer from './container/HomeContainer';
import CreateUserContainer from './container/CreateUserContainer';
import EditUserContainer from './container/EditUserContainer';
import DetailUserContainer from './container/DetailUserContainer';
export default class App extends Component {
render() {
return (
<div>
<BrowserRouter>
<NavbarComponent />
<Jumbotron />
<Routes>
<Route path='/' element={<HomeContainer/>} exact />
<Route path='/create' element={<CreateUserContainer />} exact />
<Route path='/edit/:id' element={<EditUserContainer />} exact />
<Route path='/detail/:id' element={<DetailUserContainer />} exact />
</Routes>
</BrowserRouter>
</div>
)
}
}
container/DetailUserContainer.js
import React, { Component } from 'react'
import { Container } from 'react-bootstrap'
import { connect } from 'react-redux';
import { getDetailUser } from '../actions/UserAction';
class DetailUserContainer extends Component {
componentDidMount(){
this.props.dispatch(getDetailUser(this.props.match.params.id));
}
render() {
return (
<div>
<Container className='pt-3'>
<div>
<h1>Detail</h1>
</div>
</Container>
</div>
)
}
}
export default connect()(DetailUserContainer)
reducers/users.js
import { GET_LIST_USERS, GET_DETAIL_USERS } from "../actions/UserAction";
let initialState = {
getUsers: false,
errorGetUser: false,
// DETAIL USER
getDetailUsers: false,
errorGetDetailUser: false
}
const users = (state = initialState, action) => {
switch (action.type) {
case GET_LIST_USERS:
return {
...state,
getUsers: action.payload.data,
errorGetUser: action.payload.errorMessage
};
case GET_DETAIL_USERS:
return {
...state,
getDetailUsers: action.payload.data,
errorGetDetailUser: action.payload.errorMessage
};
default:
return state;
}
}
export default users;
actions/UserAction.js
import React from 'react'
import axios from 'axios'
export const GET_LIST_USERS = "GET_LIST_USERS"
export const GET_DETAIL_USERS = "GET_DETAIL_USERS"
// GET USER
export const getListUser= () => {
return (dispatch) => {
axios.get('https://my-json-server.typicode.com/baihaqiyazid/database/users')
.then(function (response) {
dispatch({
type: GET_LIST_USERS,
payload: {
data: response.data,
errorMessage: false
}
})
})
.catch(function (error) {
dispatch({
type: GET_LIST_USERS,
payload: {
data: false,
errorMessage: error.message
}
})
})
}
}
// DETAIL USER
export const getDetailUser= (id) => {
return (dispatch) => {
axios.get('https://my-json-server.typicode.com/baihaqiyazid/database/users/' + id)
.then(function (response) {
dispatch({
type: GET_DETAIL_USERS,
payload: {
data: response.data,
errorMessage: false
}
})
})
.catch(function (error) {
dispatch({
type: GET_DETAIL_USERS,
payload: {
data: false,
errorMessage: error.message
}
})
})
}
}
reducers/index.js
import { combineReducers } from 'redux'
import users from './users'
export default combineReducers({
users
})
How to solve it?
In your component did mount use this..
componentDidMount(){
// #id params
this.props.getDetailUser(1);
}
Also connect your state using mapstate..
function mapState(state) {
return {
match: state.users // which is the name you have assigned in the combine Reducer
};
}
export default connect(mapState, {getDetailUser})(DashBoard);
refer..
Class component with Redux
https://react-redux.js.org/tutorials/connect
Before version 6, react-router-dom's Route components passed a few routing properties to any component it rendered, for example you had a route like this:
<Route path='/' component={HomeContainer} exact />
You had access to route props inside HomeContainer, like this:
this.props.match.params.id
In the above line, match is one of route props, passed from Route down to HomeContainer.
After Version 6, (which you are using), no such thing happens. The route props are no longer passed down to children, so you don't have access to this.props.match, or other route props (location, etc...).
Instead, you need to use hooks in order to have access to match, which you can't use inside Class Components. Two solutions are available:
If you want to stick to classes, Change react-router-dom version from v6 to v5, (then you have to use components like Route according to v5 API).
Get rid of Classes, instead use Functional Components, have access to hooks and keep using react-router-dom v6.

TypeError: authContext is undefined reactjs

When trying to use auth context in the App.js file it shows undefined. Please help me out in finding the mistake i am doing. Thank you for the help.
App.js
import React, { Fragment, useContext, useEffect } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Navbar from "./components/layout/Navbar";
import Home from "./components/pages/Home";
import About from "./components/pages/About";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
import Alerts from "./components/layout/Alerts";
import setAuthToken from "../src/utils/setAuthToken";
//PRIVATE ROUTE
import PrivateRoute from "./components/routing/PrivateRoute";
//CONTEXT IMPORTS
import ContactState from "./context/contact/ContactState";
import AuthState from "./context/auth/AuthState";
import AuthContext from "./context/auth/authContext";
import AlertState from "./context/alerts/AlertState";
import "./App.css";
if (localStorage.token) {
setAuthToken(localStorage.token);
}
const App = () => {
const authContext = useContext(AuthContext);
useEffect(() => {
authContext.loadUser();
//eslint-disable-next-line
});
return (
<AuthState>
<ContactState>
<AlertState>
<Router>
<Fragment>
<Navbar />
<div className='container'>
<Alerts />
<Switch>
<PrivateRoute exact path='/' component={Home} />
<Route exact path='/about' component={About} />
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
</Switch>
</div>
</Fragment>
</Router>
</AlertState>
</ContactState>
</AuthState>
);
};
export default App;
Also, in Home.js i am using context the same way but there shows no error in this file and the app runs perfectly.
Home.js
import React, { useContext, useEffect } from "react";
import Contacts from "../contact/Contacts";
import ContactForm from "../contact/ContactForm";
import ContactFilter from "../contact/ContactFilter";
import AuthContext from "../../context/auth/authContext";
const Home = () => {
const authContext = useContext(AuthContext);
useEffect(() => {
authContext.loadUser();
//eslint-disable-next-line
});
return (
<div className="grid-2">
<div>
<ContactForm />
</div>
<div>
<ContactFilter />
<Contacts />
</div>
</div>
);
};
export default Home;
authContext.js
import { createContext } from "react";
const authContext = createContext();
export default authContext;
authReducer.js
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
AUTH_ERROR,
USER_LOADED,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
CLEAR_ERRORS,
} from "../types";
const authReducer = (state, action) => {
switch (action.type) {
case USER_LOADED:
return {
...state,
isAuthenticated: true,
loading: false,
user: action.payload,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
case REGISTER_SUCCESS:
case LOGIN_SUCCESS:
localStorage.setItem("token", action.payload.token);
return {
...state,
...action.payload,
isAuthenticated: true,
loading: false,
};
case REGISTER_FAIL:
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT:
localStorage.removeItem("token");
return {
...state,
token: null,
isAuthenticated: null,
loading: false,
user: null,
error: action.payload,
};
default:
return state;
}
};
export default authReducer;
authState.js
import React, { useReducer } from "react";
import axios from "axios";
import setAuthToken from "../../utils/setAuthToken";
import AuthContext from "./authContext";
import authReducer from "./authReducer";
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
CLEAR_ERRORS,
} from "../types";
//CREATE INITIAL STATE
const AuthState = (props) => {
const initialState = {
token: localStorage.getItem("token"),
isAuthenticated: null,
user: null,
loading: true,
error: null,
};
//STATE ALLOWS US TO USE ANYTHING WE PUT IN THE STATE
//DISPATCH ALLOWS US TO DISPATCH OBJECTS,ACTIONS,METHODS OR ANYTHING TO REDUCER
const [state, dispatch] = useReducer(authReducer, initialState);
//ACTIONS
//LOAD USER - WHICH IS GOING TO TAKE CARE OF WHICH USER IS LOGGED AND ITS GOING TO HIT THAT AUTH ENDPOINT AND GET THE USER DATA
const loadUser = async () => {
// LOAD TOKEN INTO GLOBAL HEADERS
setAuthToken(localStorage.token);
try {
const res = await axios.get("/api/auth");
dispatch({
type: USER_LOADED,
payload: res.data,
});
} catch (err) {
dispatch({
type: AUTH_ERROR,
});
}
};
//REGISTER USER - WHICH SIGNS THE USER UP AND GETS A TOKEN BACK
const register = async (formData) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.post("api/user", formData, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
loadUser();
} catch (err) {
dispatch({
type: REGISTER_FAIL,
payload: err.response.data.msg,
});
}
};
//LOGIN USER - WHICH WILL LOG THE USER IN AND GET THE TOKEN
const login = async (formData) => {
const config = {
headers: {
"Content-Types": "application/json",
},
};
try {
const res = await axios.post("/api/auth", formData, config);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
loadUser();
} catch (err) {
dispatch({
type: LOGIN_FAIL,
payload: err.response.data.msg,
});
}
};
//LOGOUT - WHICH WILL DESTROY THE TOKEN AND JUST CLEAR EVERYTHIN UP
const logout = () => dispatch({ type: LOGOUT });
//CLEAR_ERRORS - TO CLEAR OUT ANY ERRORS IN THE STATE
const clearErrors = () =>
dispatch({
type: CLEAR_ERRORS,
});
//BY SURROUNDING THE COMPONENT IN THE SPECIFIC PROVIDER TAGS WE GET ACCESS TO THE STATE AND FUCNTIONS OF THE PROVIDER
return (
<AuthContext.Provider
value={{
token: state.token,
isAuthenticated: state.isAuthenticated,
loading: state.loading,
user: state.user,
error: state.error,
register,
loadUser,
login,
logout,
clearErrors,
}}
>
{props.children}
</AuthContext.Provider>
);
};
export default AuthState;
The above image shows the folder structure
You might need to wrap your App.js file in an AuthContext provider, probably in your index.js file.
ReactDOM.render(
<AuthContextProvider>
<App />
</AuthContextProvider>,
document.getElementById('root')
)

React-Redux: Access dispatch type in JSX

I have a login system with React-Redux and I want to display JSX when a specific dispatch Type is send.
For example when the login system failed the dispatch type is LOGIN_FAIL and I want to display an error message but when the authentication is successful then the dispatch type is LOGIN_SUCCESS and I want to display a success message. I already access the username trough mapStateToProps but I was curious if there is another way trough the dispatch type to do conditional rendering?
Thanks for your advice or tips on that topic.
This is my actions.js:
export const login = (email, password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ email, password });
try {
const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/jwt/create/`, body, config);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
});
dispatch(load_user());
} catch (err) {
dispatch({
type: LOGIN_FAIL
});
}
};
When i do user authentication, and i want to conditially render a component. I use React routing dom. This way i can create a PrivateRoute that will render the individual component out and redirect if the user !isAuthenticated. Check this out /
https://blog.bitsrc.io/build-a-login-auth-app-with-mern-stack-part-1-c405048e3669
This is the tutorial that you can clone the git
Private Route:
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);
MAIN APP:
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import jwt_decode from "jwt-decode";
import setAuthToken from "./utils/setAuthToken";
import { setCurrentUser, logoutUser } from "./actions/authActions";
import { Provider } from "react-redux";
import store from "./store";
import Navbar from "./components/layout/Navbar";
import Landing from "./components/layout/Landing";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
import PrivateRoute from "./components/private-route/PrivateRoute";
import Dashboard from "./components/dashboard/Dashboard";
import "./App.css";
// Check for token to keep user logged in
if (localStorage.jwtToken) {
// Set auth token header auth
const token = localStorage.jwtToken;
setAuthToken(token);
// Decode token and get user info and exp
const decoded = jwt_decode(token);
// Set user and isAuthenticated
store.dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000; // to get in milliseconds
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Redirect to login
window.location.href = "./login";
}
}
class App extends Component {
render() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Navbar />
<Route exact path="/" component={Landing} />
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Switch>
<PrivateRoute exact path="/dashboard" component={Dashboard} />
</Switch>
</div>
</Router>
</Provider>
);
}
}
export default App;

Action not triggered in React/Redux

I'm new to redux and trying to fetch content from my BackEnd API. For some reason the action I call does not reach the reducer (It's not even executed). I first thought it was because it couldn't access the store since it is has a parent component but my Provider is well configured and there is another component at the same level, and just after i started thinking it was a problem with my dispatch but honestly i don't know. I have attached the code I feel is relevant and any contributions would be highly appreciated.
actions/viewers.js
import axios from 'axios';
import { VIEWERS_LOADED, VIEWERS_ERROR } from './types';
export const loadData = async (body, http) => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
try {
const res = await axios.post(
http,
body,
config
);
return res.data;
} catch (error) {
console.log(error);
}
};
export const extractConcurrentViewers = (from, to, aggregate) => async dispatch => {
console.log("CONCURRENT VIEWERS");
const body = {
session_token: localStorage.token,
from,
to,
};
try {
let aggregateConcur = null;
const graphConccur = await loadData(body, 'http://localhost:5000/audience');
console.log('extractViews -> res_1', graphConccur);
if (aggregate !== null) {
body.aggregate = aggregate
aggregateConcur = await loadData(body, 'http://localhost:5000/audience');
}
console.log('extractaggregateViewers -> res_2', aggregateConcur);
dispatch({
type: VIEWERS_LOADED,
payload: {
graphConccur,
aggregateConcur
},
});
} catch (error) {
console.log(error);
dispatch({
type: VIEWERS_ERROR,
});
}
}
reducers/viewers.js
import {
VIEWERS_LOADED,
VIEWERS_ERROR,
} from '../actions/types';
const initialState = {
session_token: localStorage.getItem('token'),
concurrence: null,
aggConcurrence: null,
};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case VIEWERS_LOADED:
return {
...state,
...payload,
concurrence: payload.graphConccur.audience,
aggConcurrence: payload.aggregateConcur.audience,
};
case VIEWERS_ERROR:
return {
...state,
concurrence: null,
aggConcurrence: null,
};
default:
return state;
}
}
reducer/index.js
import {combineReducers} from 'redux';
import alert from './alert';
import auth from './auth'
import profile from './profile'
import chart from './chart'
import viewers from './viewers'
export default combineReducers({
alert,
auth,
profile,
chart,
viewers
});
App.js
import React, { useEffect } from 'react';
import Navbar from './components/layout/Navbar';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Landing from './components/layout/Landing';
import Login from './components/auth/Login';
import Register from './components/auth/Register';
import Alert from './components/layout/Alert';
import Dashboard from './components/dashboard/Dashboard';
import PrivateRoute from './components/routing/PrivateRouting';
import { Provider } from 'react-redux';
import store from './store';
import { loadUser } from './actions/auth';
import setAuthToken from './utils/setAuthToken'
import './App.css';
if (localStorage.token) {
setAuthToken(localStorage.token);
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser())
}, []);
return (
<Provider store={store}>
<Router>
<Navbar />
<Route exact path='/' component={Landing} />
<section className='container'>
<Alert />
<Switch>
<Route exact path='/login' component={Login} />
<Route exact path='/register' component={Register} />
<PrivateRoute exact path='/dashboard' component={Dashboard} />
</Switch>
</section>
</Router>
</Provider>
);
};
export default App;
This is where the function extractConcurrentViewers is to be called and the component supposed to use that is <Concurrent concurrence={concurrence}/> and what is really weird about is that the component just above it is implemented almost the same way but it's working.
import React, { useEffect, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Spinner from '../layout/Spinner';
import BandWidth from './BandWidth';
import Concurrent from './Concurrent';
import { extractCurrentClient } from '../../actions/profile';
import { extractchartData } from '../../actions/chart';
import { extractConcurrentViewers } from '../../actions/viewers';
const Dashboard = ({
extractCurrentClient,
extractchartData,
auth: { user },
profile: { profile, loading },
chart: { cdn, p2p, maxSum, maxCdn },
viewers: {concurrence}
}) => {
useEffect(() => {
extractCurrentClient();
extractchartData('max', 1585834831000, 1589118031000);
extractConcurrentViewers(1585834831000, 1589118031000);
}, []);
return loading && profile === null ? (
<Spinner />
) : (
<Fragment>
<h1 className='large text-primary'>Streaming</h1>
<p className='lead'>
<i className='fas fa-chart-line'></i>
Welcome {user && user.lname}
</p>
<BandWidth cdn={cdn} p2p={p2p} maxSum={maxSum} maxCdn={maxCdn} />
{/* <Concurrent concurrence={concurrence}/> */}
</Fragment>
);
};
Dashboard.propTypes = {
extractCurrentClient: PropTypes.func.isRequired,
extractchartData: PropTypes.func.isRequired,
extractConcurrentViewers: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
profile: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
auth: state.auth,
profile: state.profile,
chart: state.chart,
viewers: state.viewers,
});
export default connect(mapStateToProps, {
extractCurrentClient,
extractchartData,
extractConcurrentViewers
})(Dashboard);
store.js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
You mapped extractConcurrentViewers to props in connect but did not add it to the destructured props object. Since they share the same name, that means is you're calling your action creator without it being bound to dispatch, so it will not be delivered to your reducers.
const Dashboard = ({
extractCurrentClient,
extractchartData,
auth: { user },
profile: { profile, loading },
chart: { cdn, p2p, maxSum, maxCdn },
viewers: {concurrence},
extractConcurrentViewers // <-- add this
}) => {
Personally I don't destructure my props and this is one reason. I prefer the code to be explicit about where values and functions are coming from props.extractConcurrentViewers . But that's my preference.

call mapDispatchToProps inside axios success not working

I am trying to navigate to another page after a successful axios call which dispatch the action to change the state. But, I am sure that i wrongly call dispatch inside the axios call which result below error. I have tried many other ways like using ES6 arrow function to call the dispatch method but never works.
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
App.js
import React, { Component } from 'react';
import axios from 'axios';
import history from './History';
import { Redirect } from "react-router-dom";
import './App.css';
import { connect } from 'react-redux';
import * as actionType from './reducer/action';
class App extends Component {
constructor(){
super();
this.state = {
username: '',
password: '',
loginData: []
};
this.handleUserChange = this.handleUserChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event){
event.preventDefault();
axios.post('https://api.github.com/user',{}, {
auth: {
username: this.state.username,
password: this.state.password
}
}).then((response) => {
console.log(response.data);
this.props.onLoginAuth(true);// This is where i am getting ERROR
//history.push({pathname: '/home', state: { detail: response.data }});
//history.go('/home');
this.setState({
loginData : response.data,
});
}).catch(function(error) {
console.log('Error on Authentication' + error);
});
}
handleUserChange(event){
this.setState({
username : event.target.value,
});
}
handlePasswordChange = event => {
this.setState({
password: event.target.value
});
}
render() {
if(this.props.authenticated){
console.log("Redirecting to Home page " + this.props.authenticated);
return <Redirect to={{ pathname: '/home', state: { detail: this.state.loginData } }}/>
}
return (
<div className='loginForm'>
<form onSubmit={this.handleSubmit}>
<label>
username :
<input type="text" value={this.state.username} onChange={this.handleUserChange} required/>
</label>
<label>
password :
<input type="password" value={this.state.password} onChange={this.handlePasswordChange} required/>
</label>
<input type="submit" value="LogIn" />
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
authenticated: state.authenticated
};
}
const mapDispatchToProps = dispatch => {
return {
onLoginAuth: (authenticated) => dispatch({type: actionType.AUTH_LOGIN, authenticated:authenticated})
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import PrivateRoute from './PrivateRoute';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import {
Router,
Redirect,
Route,
Switch
} from "react-router-dom";
import Home from './Home';
import User from './User';
import history from './History';
import reducer from './reducer/Login';
const store = createStore(reducer);
ReactDOM.render(
<Provider store= {store} >
<Router history={history}>
<Switch>
<Route path="/" exact component={App} />
<PrivateRoute path="/home" component={Home} />
<PrivateRoute path="/user" component={User} />
</Switch>
</Router>
</Provider>,
document.getElementById('root'));
registerServiceWorker();
Home.js
import React, { Component } from 'react';
import axios from 'axios';
import Autosuggest from 'react-autosuggest';
import './Home.css';
import history from './History';
// When suggestion is clicked, Autosuggest needs to populate the input
// based on the clicked suggestion. Teach Autosuggest how to calculate the
// input value for every given suggestion.
const getSuggestionValue = suggestion => suggestion;
// Use your imagination to render suggestions.
const renderSuggestion = suggestion => (
<div>
{suggestion}
</div>
);
class Home extends Component {
constructor(props) {
super(props);
// Autosuggest is a controlled component.
// This means that you need to provide an input value
// and an onChange handler that updates this value (see below).
// Suggestions also need to be provided to the Autosuggest,
// and they are initially empty because the Autosuggest is closed.
this.state = {
value: '',
suggestions: [],
timeout: 0
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
console.log('=====++++ ' + newValue);
};
onSuggestionSelected = (event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method }) => {
console.log("Get the user +++++++++ " + suggestionValue);
if(suggestionValue && suggestionValue.length >= 1){
axios.get('https://api.github.com/users/'+ suggestionValue)
.then((response) => {
console.log("user selected : "+ response.data.avatar_url);
history.push({pathname: '/user', state: { detail: response.data }});
history.go('/user');
}).catch(function(error) {
console.log('Error on Authentication' + error);
});
}
};
// Autosuggest will call this function every time you need to update suggestions.
// You already implemented this logic above, so just use it.
onSuggestionsFetchRequested = ({ value }) => {
if(this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.getSuggestions();
}, 500);
};
getSuggestions = () =>{
if(this.state.value && this.state.value.length >= 1){
axios.get('https://api.github.com/search/users',{
params: {
q: this.state.value,
in:'login',
type:'Users'
}
}).then((response) => {
console.log("users login : "+ response.data.items);
const userNames = response.data.items.map(item => item.login);
console.log("===== " + userNames);
this.setState({
suggestions: userNames
})
}).catch(function(error) {
console.log('Error on Authentication' + error);
});
}
};
// Autosuggest will call this function every time you need to clear suggestions.
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render(){
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Type a userName',
value,
onChange: this.onChange
};
return (
<div>
<div>
Home page {this.props.location.state.detail.login}
</div>
<div>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
onSuggestionSelected={this.onSuggestionSelected}
/>
</div>
</div>
);
}
}
export default Home;
PrivateRouter.js
import React from 'react';
import {
Redirect,
Route,
} from "react-router-dom";
const PrivateRoute = ({ component: Component, ...rest}) => (
<Route
{...rest}
render={props =>
props.authenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/",
state: { from: props.location }
}}
/>
)
}
/>
);
export default PrivateRoute;
Can you help me how i can call onLoginAuth from axios?
I don't think that the problem comes from the axios call. Your axios call the dispatch which update the state authenticated. So everytime the state authenticated is updated, the render method of App component is called.
The condition if(this.props.authenticated) is verified, and Redirect to /home is called. But somehow, your router routes again to /. The App component is rerender. The condition if(this.props.authenticated) is true again, and the routes routes again to App. It creates an infinite loop, that's why you see the message Maximum update depth exceeded.
In the index.js, for testing purpose, replace all PrivateRoute by Route to see if the route works correctly. If it works, the problem may come from your PrivateRoute component.

Categories