I've got a useEffect that sets state.user, which allows me to persist state and keep a user logged in as they navigate around. Auth.currentSession and Auth.currentAuthenticatedUser (line 105-106) are part of the AWS Amplify library and basically pull from localStorage (which I just learned about yesterday). So useEffect runs, calls the dispatch on line 110. line 115 prints the returned data from Auth.currentAuthenticatedUser, but 116 prints something equivalent to initialState when I expect to see values equivalent to "user" because "user" is sent as payload with the dispatch to "LOGIN". I'm guessing the "[ ]" argument in the useEffect has something to do with it, but I can't figure out what value to put in there to keep it from going into an infinite loop. My main goal is to save data to state and use it in other components with useContext.
import React, { useEffect } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.css';
import MainNavbar from './components/Navbar';
import Home from './components/Home';
import LogIn from './components/auth/LogIn';
// import Register from './components/auth/Register';
import ForgotPassword from './components/auth/ForgotPassword';
import ForgotPasswordVerification from './components/auth/ForgotPasswordVerification';
import ChangePassword from './components/auth/ChangePassword';
import ChangePasswordConfirm from './components/auth/ChangePasswordConfirm';
// import Welcome from './components/auth/Welcome';
// import Ingredients from './components/Ingredients';
import Recipes from './components/Recipes';
import Footer from './components/Footer';
import { Auth } from 'aws-amplify';
// import axios from 'axios';
import { library } from '#fortawesome/fontawesome-svg-core';
import { faCheckSquare, faCoffee, faEdit } from '#fortawesome/free-solid-svg-icons';
library.add( faCheckSquare, faCoffee, faEdit);
// const config = require('./config.json');
export const AuthContext = React.createContext();
const initialState = {
isAuthenticated: false,
// isAuthenticating: true,
user: null,
userSettings: null,
userRec: null,
userIng: null,
userData: null,
defaultIng: null
}
const reducer = (state, action) => {
switch (action.type) {
case "LOGIN":
console.log("LOGIN");
return {
...state,
isAuthenticated: true,
user: action.payload
};
case "LOGOUT":
console.log("LOGOUT");
return {
...state,
isAuthenticated: false,
user: null
};
case "INITIALIZE_USER_DATA":
const userId = state.user.attributes.sub;
console.log(state.user);
let userRec = [];
let userIng = [];
let userData = [];
let defaultIng = [];
async function initializeUserData() {
try {
const params = {
"userId": userId,
"sett": true
};
let res1 = await axios.patch(`${config.api.invokeUrl}/user/configsettings`, params);
let res2 = await axios.post(`${config.api.invokeUrl}/recipes/${userId}`, params);
let res3 = await axios.get(`${config.api.invokeUrl}/ingredients`);
console.log(res1);
console.log(res2);
console.log(res3);
defaultIng = res3.data.sort((a, b) => (a.iName > b.iName) ? 1 : -1);
// this.setUserSettings(res1.data.Attributes); //4-13-2020, Myles - seems to be the same data coming from res2 and assigned to userData.
let arr1 = res2.data;
arr1.forEach( item => {
if (item.sk.startsWith("USER-")) {
userData.push(item);
} else if (item.sk.startsWith("REC-")) {
userRec.push(item);
} else if (item.sk.startsWith("ING-")) {
userIng.push(item);
}
});
} catch (error) {
console.log(error);
}
}
initializeUserData();
return {
...state,
isAuthenticated: true,
user: action.payload,
userRec: userRec,
userIng: userIng,
userData: userData,
defaultIng: defaultIng
};
default:
return state;
}
};
function App() {
const [state, dispatch] = React.useReducer(reducer, initialState);
async function authStatus() {
try {
let session = await Auth.currentSession();
let user = await Auth.currentAuthenticatedUser();
console.log(user);
console.log(session);
if (session && user) {
dispatch({
type: "LOGIN",
payload: user
});
};
console.log(user);
console.log(state);
}catch(error) {
console.log(error);
}
};
useEffect(() => {
authStatus();
},[]);
return (
<AuthContext.Provider
value={{
state,
dispatch
}}
>
<div className="App">
<Router>
<div className="container">
<MainNavbar />
<Switch>
<Route exact path="/" component = {Home} />
<Route exact path="/login" render={() => <LogIn />} />
<Route exact path="/recipes" render={() => <Recipes />} />
<Route exact path="/forgotpassword" render={() => <ForgotPassword />} />
<Route exact path="/forgotpasswordverification" render={() => <ForgotPasswordVerification />} />
<Route exact path="/changepassword" render={() => <ChangePassword />} />
<Route exact path="/changepasswordconfirmation" render={() => <ChangePasswordConfirm />} />
</Switch>
<Footer />
</div>
</Router>
</div>
</AuthContext.Provider>
);
}
export default App;
I'm guessing the [] argument in the useEffect has something to do with it (state is always initialState), but I can't figure out what value to put in there to keep it from going into an infinite loop!!
A common question that taunts many of us when using the famous useEffect hook. Indeed, the useEffect has something to do with your state updates, if any side effects actually change your state.
Note: If you pass an empty array [], the props and state inside the effect will always have their initial values.
Quick solution: (+)
Either add state to the useEffect dependency array
or console.log(state) within the return() statement
// summarized version, to focus on issue at hand
function App() {
const [state, dispatch] = React.useReducer(reducer, initialState);
// invoked within the useEffect()
async function authStatus() {
try {
let session = await Auth.currentSession();
let user = await Auth.currentAuthenticatedUser();
if (session && user) { dispatch({ type: "LOGIN", payload: user }) };
console.log(user);
console.log(state); // thus, this is always initialState (see useEffect() below)
} catch(error) {
console.log(error);
}
};
useEffect(() => {
authStatus();
// If you pass an empty array ([]), the props and state
// inside the effect will always have their initial values.
- }, []);
// Either, refactor the dependency array to include "state"
+ }, [state]);
return (
<AuthContext.Provider value={{ state, dispatch }}>
<div className="App">
+ {console.log(state)} // Or, log "state" here
<Router>{/* Routes */}</Router>
</div>
</AuthContext.Provider>
);
}
export default App;
Here's a detailed explanation:
Credit to the React team who did a good job to explain the useEffect() and it's effects:)
Note that by default, useEffect runs both after the first render and after every update. Sometimes you don't want the useEffect to always run after render.How do I make that happen?
Thus to optimize performance, you can skip effects by passing an array [] as an optional second argument to useEffect. Again, the React team recommends using the exhaustive-deps rule as part of the eslint-plugin-react-hooks package. It warns when dependencies are specified incorrectly and suggests a fix. Note, this is included, by default, if you use create-react-app.
If you use this optimization (mentioned above), make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders. That's why if you console.log() here, props and state will always have their initial values.
Related
I was trying after any user registered or login into this website user will be confirmed in the server and the response will be stored in local storage. Based on the user I will check in the local storage is user available and then login into the site. It works fine when users newly register and get a server response it saves local storage and the user Navigates to the Profile. But after I check the same user to login form I got a response from the server but it is not going saved in local storage so I can not able to check the user in local storage and it did not navigate the profile section.
** here's my App js file **
function App() {
const user = useSelector((state) => state.authReducer.authData);
console.log(user);
return (
<div className="App">
<div className="blur" style={{ top: "-18%", right: 0 }}></div>
<div className="blur" style={{ top: "36%", left: "-8rem" }}></div>
<Routes>
<Route
path="/"
element={user ? <Navigate to="home" /> : <Navigate to="auth" />}
/>
<Route
path="/home"
element={user ? <Home /> : <Navigate to="../auth" />}
/>
<Route
path="/auth"
element={user ? <Navigate to="../home" /> : <Auth />}
/>
</Routes>
</div>
auth Reducer file
state = { authData: null, loading: false, error: false },
action
) => {
switch (action.type) {
case "AUTH_START":
return { ...state, loading: true, error: false };
case "AUTH_SUCCESS":
localStorage.setItem("profile", JSON.stringify({ ...action?.data }));
return { ...state, authData: action.data, loading: false, error: false };
case "AUTH_FAIL":
return { ...state, loading: false, error: true };
default:
return state;
}
};
export default authReducer;
**Index.js file **
import authReducer from "./AuthReducer";
export const reducers = combineReducers({authReducer})
**Redux store file **
legacy_createStore as createStore,
applyMiddleware,
compose,
} from "redux";
import thunk from "redux-thunk";
import { reducers } from "../Reducer";
function saveToLocalStorage(store) {
try {
const serializedStore = JSON.stringify(store);
window.localStorage.setItem("store", serializedStore);
} catch (e) {
console.log(e);
}
}
function loadFromLocalStorage() {
try {
const serializedStore = window.localStorage.getItem("store");
if (serializedStore === null) return undefined;
return JSON.parse(serializedStore);
} catch (e) {
console.log(e);
return undefined;
}
}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const persistedState = loadFromLocalStorage();
const store = createStore(
reducers,
persistedState,
composeEnhancers(applyMiddleware(thunk))
);
store.subscribe(() => saveToLocalStorage(store.getState()));
export default store;
And finally, I got an error in my console log that is
Actions must be plain objects. Instead, the actual type was: 'Promise'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions *
Check your App.jsx component located on src/pages/Auth/Auth.jsx. Fix your login import syntax like this.
import { signUp, logIn } from "../../Action/AuthAction";
To store redux data in localStorate or sessionStorage try to use redux-persist
Wrap your App.js with PersistGate from redux-persist
and then in store file instead of create a function saveToLocalStorage define a rootConfig where storage comes from redux-persist
const rootPersistConfig = {
key: 'root',
storage,
};
if you want to store data in sessionStorage
const rootPersistConfig = {
key: 'root',
storage: sessionStorage,
};
use it as
const persistedReducer = persistReducer(rootPersistConfig, authReducer);
now use persistedReducer as reducers in your index.js file.
// if you want to combine multiple reducer then you can do this like
const rootReducer = combineReducers({
user: userReducer,
cart: persistReducer(cartPersistConfig, cartReducer),
});
and then make a persistedReducer like this insted of authReducer you'll put your combined rootReducer
const persistedReducer = persistReducer(rootPersistConfig, rootReducer);
for more details check redux-persist
I'm facing a very weird issue where my response comes back correctly during the first log but later on it changes to a 1 when the console.log(data); logs data to the console.
The screenshot shows the pattern:
If you expand the very first array underneath where it says data below from userEffect inside Gallery.js , the data shows up as intended
Then there's the response array from App.js which also appears as intended.
Lastly, the response magically gets converted to a 1 from inside Gallery.js and I'm not sure how. I've ran out of options on how to troubleshoot this and resolve it where it doesn't get converted to a 1.
What am I doing wrong? I'm open to any code improvements and any other suggestions for better practice :).
Here's App.js:
import { useEffect, useState } from 'react';
import { BrowserRouter as Router, Switch, Route, useHistory} from 'react-router-dom';
import ReactDOM from 'react-dom';
import '../../sass/HomePage/homePage.scss';
import LoginRegister from "./LoginRegister/LoginRegister";
import Gallery from "./Gallery/Gallery";
import Cookies from 'js-cookie';
const App = () => {
const [uploadsData, setUploadsData] = useState([]);
let { push } = useHistory();
let authToken = Cookies.get('token');
useEffect(() => {
getUploads();
},[])
function getUploads() {
const headers = {
"Accept": 'application/json',
"Authorization": `Bearer ${authToken}`
}
axios.get('http://localhost:8005/api/get-uploads', {headers})
.then(resp => {
let uData = uploadsData.push(resp)
setUploadsData(uData);
console.log(uploadsData);
if (authToken !== null) {
push('/gallery');
} else {
console.log("User's NOT authenticated, returning to login view");
push('/');
}
}).catch(error => {
console.log(error);
})
}
return (
<>
<Switch>
<Route exact path="/" component={LoginRegister} />
<Route component={() => <Gallery data={uploadsData}/>} />
</Switch>
</>
);
}
export default App;
if (document.getElementById('example')) {
ReactDOM.render(<Router><App/></Router>, document.getElementById('example'));
}
Here's Gallery.js:
import React, {useEffect} from 'react';
const Gallery = ( {data} ) => {
useEffect(() => {
console.log("FROM App.js INSIDE Gallery.js");
console.log(data);
}, []);
return (
<>
<h1>test</h1>
</>
);
}
export default Gallery;
The push method of array always returns the new length of the array( in your case is 1) which you are setting it to the uploadsData state.
let uData = uploadsData.push(resp)
setUploadsData(uData);
And you should never set the value of the state like that(mutable). Instead you should update the state immutably like this:
let uData = [...uploadsData,resp];
setUploadsData(uData);
I have a UserContext that is set when App renders. App retrieves the current user from a server, and then sets the user context in a provider.
So whenever I navigating to a link, App renders, get's the current user from the server, and sets it. This allows all the children have access to the user.
Problem with <Redirect>
But I'm running into a problem when I use <Redirect>.
If the user updates on the server, then App needs to re-render in order to get the updated user object.
But on a redirect App doesn't re-render which leads to an outdated user context until the user refreshes the page in order to re-render App.
Example: Login to see your profile
In my code below I have a login button. When the user logs in the page redirects to their profile.
But even though the user is successfully logged in on the server, the user context hasn't updated. This is because redirect doesn't re-render App.
Is there a way to get redirect to re-render app or some other solution?
Code
The relevant code is below.
The full code is available on the repo here. Download, run npm i, npm start, and then either select and play Compounded Server/React in the debugger or run node currentUserServer/server.js to start the server without the debugger tools.
Frontend
App.js
import React, { useEffect, useContext, useState } from "react";
import { UserContext } from "./contexts/UserContext";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Login from "./Login";
import Profile from "./Profile";
const currentUser = async () => {
const user = await fetch("/users/current", {}).then(async (res) => {
const userJson = await res.json();
return userJson;
});
return user;
};
export default function App() {
const [user, setUser] = useState(null);
useEffect(() => {
currentUser().then((user) => {
setUser(user);
});
}, []);
return (
<Router>
<div className="App">
<UserContext.Provider value={user}>
<Switch>
<Route path="/profile">
<Profile />
</Route>
<Route path="/">
<Login />
</Route>
</Switch>
</UserContext.Provider>
</div>
</Router>
);
}
Login.js
import React, { useContext, useState } from "react";
import { Redirect } from "react-router-dom";
import { UserContext } from "./contexts/UserContext";
export default function Login() {
const [next, setNext] = useState(false);
const currentUser = useContext(UserContext);
return (
<div>
Logged In:{" "}
{!currentUser || currentUser.message === "not logged in"
? "No One"
: currentUser.username}{" "}
<br></br>
<button
onClick={() => {
fetch("/login", { method: "POST" }).then((res) => {
if (res.status === 201) setNext(true);
});
}}
>
Login
</button>
<button
onClick={() => {
fetch("/logout", { method: "DELETE" });
}}
>
Logout
</button>
{next && <Redirect to="/profile" />}
</div>
);
}
Profile.js
import React, { useContext } from "react";
import { UserContext } from "./contexts/UserContext";
export default function Profile() {
const currentUser = useContext(UserContext);
return (
<div>
{currentUser && !currentUser.message
? "You're logged in and can edit your profile."
: "You're not logged in."}
</div>
);
}
I found two solutions solution.
#1 Quick fix but not as good: setRefresh
This works but seems to nullify the whole point of using context because we have to pass the state down.
I created a refresh state on App.js that triggers useEffect.
useEffect(() => {
if (refresh) {
currentUser().then((user) => {
setUser(user);
setRefresh(false);
});
}
}, [refresh]);
Now I can pass setRefresh as a prop to Login in order trigger useEffect to run again.
Full code here
#2 Better! setUser and getUser in context
This is sort of like the solution above but makes it so you're still taking advantage of context. You put setUser and getUser method on the context- inspired by this answer.
In app.js we create a user state. We set the value of UserContext to an object. The object contains user and setUser. And we also pass in getUser, a function that request the current user from the server.
export default function App() {
const [user, setUser] = useState(null);
const getUser = async () => {
const user = await fetch("/users/current", {}).then(async (res) => {
const userJson = await res.json();
return userJson;
});
return user;
};
const value = { user, setUser, getUser };
Here the object is passed to the provider.
<UserContext.Provider value={value}>
Now we have access to user, setUser, getUser anywhere there is a UserContext consumer.
const { user, setUser, getUser } = useContext(UserContext);
We can use getUser to ping the server for the current user. Then set that result as the user.
setUser(await getUser());
export default function Login(props) {
const [next, setNext] = useState(false);
const { user, setUser, getUser } = useContext(UserContext);
return (
<div>
Logged In:{" "}
{!user || user.message === "not logged in" ? "No One" : user.username}{" "}
<br></br>
<button
onClick={() => {
fetch("/login", { method: "POST" }).then(async (res) => {
if (res.status === 201) {
setUser(await getUser());
setNext(true);
}
});
}}
>
Login
</button>
Full Code here
I am new in react.I am trying to use react-redux style from the beginning.
Below is what I tried for a simple product listing page.
In my App.js for checking if the user is still logged in.
class App extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentDidMount() {
if (isUserAuthenticated() === true) {
const token = window.localStorage.getItem('jwt');
if (token) {
agent.setToken(token);
}
this.props.appLoad(token ? token : null, this.props.history);
}
}
render() {
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
isUserAuthenticated() === true
? <Component {...props} />
: <Redirect to='/logout' />
)} />
)
return (
<React.Fragment>
<Router>
<Switch>
{routes.map((route, idx) =>
route.ispublic ?
<Route path={route.path} component={withLayout(route.component)} key={idx} />
:
<PrivateRoute path={route.path} component={withLayout(route.component)} key={idx} />
)}
</Switch>
</Router>
</React.Fragment>
);
}
}
export default withRouter(connect(mapStatetoProps, { appLoad })(App));
In my action.js appLoaded action is as under
export const appLoad = (token, history) => {
return {
type: APP_LOAD,
payload: { token, history }
}
}
reducer.js for it
import { APP_LOAD, APP_LOADED, APP_UNLOADED, VARIFICATION_FAILED } from './actionTypes';
const initialState = {
appName: 'Etsync',
token: null,
user: null,
is_logged_in: false
}
const checkLogin = (state = initialState, action) => {
switch (action.type) {
case APP_LOAD:
state = {
...state,
user: action.payload,
is_logged_in: false
}
break;
case APP_LOADED:
state = {
...state,
user: action.payload.user,
token: action.payload.user.token,
is_logged_in: true
}
break;
case APP_UNLOADED:
state = initialState
break;
case VARIFICATION_FAILED:
state = {
...state,
user: null,
}
break;
default:
state = { ...state };
break;
}
return state;
}
export default checkLogin;
And in Saga.js I have watched every appLoad action and performed the operation as under
import { takeEvery, fork, put, all, call } from 'redux-saga/effects';
import { APP_LOAD } from './actionTypes';
import { appLoaded, tokenVerificationFailed } from './actions';
import { unsetLoggeedInUser } from '../../helpers/authUtils';
import agent from '../agent';
function* checkLogin({ payload: { token, history } }) {
try {
let response = yield call(agent.Auth.current, token);
yield put(appLoaded(response));
} catch (error) {
if (error.message) {
unsetLoggeedInUser();
yield put(tokenVerificationFailed());
history.push('/login');
} else if (error.response.text === 'Unauthorized') {
unsetLoggeedInUser();
yield put(tokenVerificationFailed());
}
}
}
export function* watchUserLogin() {
yield takeEvery(APP_LOAD, checkLogin)
}
function* commonSaga() {
yield all([fork(watchUserLogin)]);
}
export default commonSaga;
After that for productLists page my code is as under
//importing part
class EcommerceProductEdit extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentDidMount() {
**//seeing the props changes**
console.log(this.props);
this.props.activateAuthLayout();
if (this.props.user !== null && this.props.user.shop_id)
this.props.onLoad({
payload: Promise.all([
agent.Products.get(this.props.user),
])
});
}
render() {
return (
// JSX code removed for making code shorter
);
}
}
const mapStatetoProps = state => {
const { user, is_logged_in } = state.Common;
const { products } = state.Products.products.then(products => {
return products;
});
return { user, is_logged_in, products };
}
export default connect(mapStatetoProps, { activateAuthLayout, onLoad })(EcommerceProductEdit);
But in this page in componentDidMount if I log the props, I get it three time in the console. as under
Rest everything is working fine. I am just concerned,the code i am doing is not up to the mark.
Any kinds of insights are highly appreciated.
Thanks
It's because you have three state updates happening in ways that can't batch the render.
You first render with no data. You can see this in the first log. There is no user, and they are not logged in.
Then you get a user. You can see this in the second log. There is a user, but they are not logged in.
Then you log them in. You can see this in the third log. There is a user, and they are logged in.
If these are all being done in separate steps and update the Redux store each step you'll render in between each step. If you however got the user, and logged them in, and then stored them in the redux state in the same time frame you'd only render an additional time. Remember React and Redux are heavily Async libraries that try to use batching to make sure things done in the same time frame only cause one render, but sometimes you have multiple network steps that need to be processed at the same time. So no you're not doing anything wrong, you just have a lot of steps that can't easily be put into the same frame because they rely on some outside resource that has its own async fetch.
I' learning to work with Redux, React & React Router.
I've face with such a problem:
When I redirect from "/" to the URL like "/details/{id}" using Link - I see that a wrong action creator is called. Action creator from "/" component is called indead of component's one in "/details/{id}.
But if I refresh my page, everything will be fine. Correct action is called.
Routing with Link: <Link to={/details/${this.props.movie.id}}>
Other bug: if I press "Back" from this page to return to "/", I will get an error, that my props' data are undefined.
Like, props are empty and the action creator for http-request is not called (?) from componentDidMount().
But if I refresh my page again, everything will be fine again.
What's wrong with routing?? Or redux?
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Routes />
</BrowserRouter>
</Provider>,
document.getElementById('root'));
const Routes = () => {
return <div>
<Route exact path="/" component={Main} />
<Route path="/details/:id" component={MovieDetailsContainer} />
</div>;
}
"/":
class MoviesDiscover extends Component {
componentDidMount() {
this.props.dicoverMovies();
}
render() {
...
}
}
const mapStateToProps = (state) => {
return {
items: state.movieItems,
hasErrored: state.movieHasErrored,
isLoading: state.movieIsLoading,
};
};
const mapDispatchToProps = (dispatch) => {
return {
dicoverMovies: (url) => dispatch(dicoverMovies(url))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MoviesDiscover);
"details/{id}":
class MovieDetailsContainer extends Component {
componentDidMount() {
var id = this.props.match.params.id;
this.props.getMovie(id); // called from MoviesDiscover instead of this, after routing
}
render() {
var id = this.props.match.params.id;
...
}
}
const mapStateToProps = (state) => {
return {
item: state.movieItems,
hasErrored: state.movieHasErrored,
isLoading: state.movieIsLoading,
};
};
const mapDispatchToProps = (dispatch) => {
return {
getMovie: (url) => dispatch(getMovieDetails(url))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MovieDetailsContainer);
Actions (the same for two reqests - actions for results, error and lodaing):
...
export function moviesDataSuccess(items) {
return {
type: MOVIES_RESULTS,
isLoading: false,
items: items
};
}
export function dicoverMovies() {
return (dispatch) => {
dispatch(moviesIsLoading(true));
API.get(URL_MOVIE_DISCOVER)
.then(response => {
dispatch(moviesIsLoading(false));
dispatch(moviesDataSuccess(response.data));
})
.catch(e => {
console.log(e);
dispatch(moviesHasErrored(true))
});
};
}
export function getMovieDetails(id) {
return (dispatch) => {
dispatch(moviesIsLoading(true));
API.get(URL_MOVIE_DETAILS(id))
.then(response => {
dispatch(moviesIsLoading(false));
dispatch(moviesDataSuccess(response.data));
})
.catch(e => {
console.log(e);
dispatch(moviesHasErrored(true))
});
};
}
Reducers:
export function movieHasErrored(state = false, action) {
switch (action.type) {
case MOVIES_ERROR:
return action.hasErrored;
default:
return state;
}
}
export function movieIsLoading(state = false, action) {
switch (action.type) {
case MOVIES_LOADING:
return action.isLoading;
default:
return state;
}
}
export function movieItems(state = {}, action) {
switch (action.type) {
case MOVIES_RESULTS:
return action.items;
default:
return state;
}
}
const rootReducer = combineReducers({
movieHasErrored,
movieIsLoading,
movieItems
});
I will be happy to all the recommendations. Thank you.
React router matches the closes URL first, I think the issue is the order of your component.
I suggest you order them like this and it should get resolved:
const Routes = () => {
return <div>
<Route path="/details/:id" component={MovieDetailsContainer} />
<Route exact path="/" component={Main} />
</div>;
}
Also React Router has a component for switching between URLs that you can take advantage if your URLs are ambiguous:
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
...
<Switch>
<Route path="/about" component={About} />
<Route path="/company" component={Company} />
<Route path="/:user" component={User} />
</Switch>
PS: Sorry I just wanted to comment instead of posting this answer if this is not the answer you're looking for, I just don't yet have reputation to comment directly :|
My mistake was in other place.
The problem was, I was using the same action-type for list results and details reasult.
When I was opening a page for details results, I've got props from previous page with list results ('cause they use the same action-type). Before my Details Component would fetch new data.
And my code was failing in render with wrong data.