React Router 4 Async Rendering - javascript

I am following the guide on React Router 4 for Redirect(Auth) and I am having trouble rendering base on the promise the ajax returns. I'm not sure why my rendering inside the promise is not being returned. Could someone point me to the right direction?
import React from 'react';
import {
Route,
Redirect,
withRouter
} from 'react-router-dom';
import HeaderContainer from '../containers/HeaderContainer';
const PrivateRoute = ({ component: Component, ...props }) => {
const validated = (rest) => {
props.fetchUser()
.then(() => {
return (
<div>
<HeaderContainer />
<Component {...rest}/>
</div>
)
})
.catch(()=> {
return (
<Redirect to={{
pathname: '/signin',
state: { from: props.location }
}}/>
)
}
);
}
return (
<Route {...props} render={rest => {
return (
<div>
{ validated(rest) }
</div>
)
}}/>
)
}
export default withRouter(PrivateRoute);
My routes look like this
const Root = ({ store }) => {
return (
<Provider store={ store }>
<BrowserRouter onUpdate={() => window.scrollTo(0, 0)}>
<div className="root">
<Switch>
<Route exact path="/signin" component={SignInContainer}/>
<PrivateRouteContainer exact path="/" component={HomePageContainer} />
</Switch>
</div>
</BrowserRouter>
</Provider>
)
};

Thats because promise cannot return value, it only returns Promise. Instead it execute callbacks. Here is some explanation.
You could rearrange your code to somewhat like this:
class PrivateRoute extends React.Component {
constructor(props){
super(props);
this.state = {
isFetching: true,
isSuccess: null,
};
}
componentDidMount() {
this.props.fetchUser()
.then(() => {
this.setState({ isFetching: false, isSuccess: true });
})
.catch(()=> {
this.setState({ isFetching: false, isSuccess: false });
});
}
render() {
const { isFetching, isSuccess } = this.state;
return (
<Route {...this.props} render={rest => {
const success = (
<div>
<HeaderContainer />
<Component {...rest}/>
</div>
);
const error = (
<Redirect to={{
pathname: '/signin',
state: { from: this.props.location }
}}/>
);
if(isFetching) {
return null;
}
return isSuccess ? success : error;
}}/>
)
}
}
Notice that Promise doesn't return anything it just executing a callback which triggers rerender with new data in state.

Related

React onChange function to LogIn not working

I have been on this for 10 hours and It is frustrating to say I have no clue what I am missing.
I am making a simple react app with a Sign-up and log-in function,
I have successfully created mypage, and sign up and here are two things I want to achieve and cannot get it done:
1. it should be called handleResponseSuccess callback after log in
2. it should log in successfully when button click
even a simple point out will be a huge help.. thank you
App.js
import React from "react";
import { Switch, Route, Redirect, withRouter } from "react-router-dom";
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Mypage from "./pages/Mypage";
import axios from "axios";
class App extends React.Component {
constructor(props) {
super(props);
this.staate = {
isLogin: false,
userinfo: null,
}
this.handleResponseSuccess = this.handleResponseSuccess.bind(this)
this.handler = this.handler.bind(this)
}
handleResponseSuccess() {
axios.get('http://localhost:4000/user')
.then((res) => {
this.setState({ isLogin: true, userinfo: { ...res.data }, });
this.props.history.push("/");
});
}
handler() {
axios.post('https://localhost:4000/signout', null, {
withCredentials: true,
})
.then((res) => {
this.setState({
isLogin: false,
});
this.props.history.push('/')
})
.catch((err) => alter(err));
}
render() {
const { isLogin, userinfo } = this.state;
return (
<div>
<Switch>
<Route
path='/login'
render={() => (
<Login handleResponseSuccess={this.handleResponseSuccess} />
)}
/>
<Route exact path='/signup' render={() => <Signup />} />
<Route
exact
path='/mypage'
render={() => <Mypage userinfo={userinfo} handleLogout={this.handler}/>}
/>
<Route
path='/'
render={() => {
if (isLogin) {
return <Redirect to='/mypage' />;
}
return <Redirect to='/login' />;
}}
/>
</Switch>
</div>
);
}
}
export default withRouter(App);
login.js
import React from "react";
import { Link, withRouter } from "react-router-dom";
import axios from "axios";
axios.defaults.withCredentials = true;
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
errorMessage: ""
};
this.handleInputValue = this.handleInputValue.bind(this);
this.handleLogin = this.handleLogin.bind(this)
}
handleInputValue = (key) => (e) => {
this.setState({ [key]: e.target.value });
};
handleLogin = () => {
if (this.state.email.length === 0 || this.state.password.length === 0) {
return this.setState({ errorMessage : "enter email and password"})
}
axios.defaults.withCredentials = true;
axios.post("https://localhost:4000/signin", {
email: this.state.email,
password : this.state.password
}).then(() => {
this.props.handleResponseSuccess()
})
};
render() {
return (
<div>
<center>
<h1>Sign In</h1>
<form onSubmit={(e) => e.preventDefault()}>
<div>
<span>email</span>
<input type='email' onChange={this.handleInputValue("email")}></input>
</div>
<div>
<span>password</span>
<input type='password' onChange={this.handleInputValue("password")}></input>
</div>
<div>
<Link to='/signup'>no id?</Link>
</div>
<button className='btn btn-login' type='submit' onClick={this.handleLogin}>
log in
</button>
{this.state.errorMessage ? <div className="alert-box">{this.state.errorMessage}</div> : null}
</form>
</center>
</div>
);
}
}
export default withRouter(Login);
in your code in App.js
this.staate = {
isLogin: false,
userinfo: null,
}
and you are calling it as
const { isLogin, userinfo } = this.state;
maybe this could be of some help

the logout component not rendering after the authenticated is turned to true this is similar code as from react-router docs

I just tried to build the react-router docs ex on browser but there is problem in AuthButton component it isn't showing signOut button when the isAuthenticated turns true
import React from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
useLocation,
} from 'react-router-dom';
export default function AuthExample() {
return (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to='/public'>Public Page</Link>
</li>
<li>
<Link to='/protected'>Protected Page</Link>
</li>
</ul>
<Switch>
<Route path='/public'>
<PublicPage />
</Route>
<Route path='/login'>
<LoginPage />
</Route>
<PrivateRoute path='/protected'>
<ProtectedPage />
</PrivateRoute>
</Switch>
</div>
</Router>
);
}
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
fakeAuth.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
fakeAuth.isAuthenticated = false;
setTimeout(cb, 100);
},
};
function AuthButton() {
let history = useHistory();
return fakeAuth.isAuthenticated ? (
<p>
Welcome!{' '}
<button
onClick={() => {
fakeAuth.signout(() => history.push('/'));
}}>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
);
}
function PrivateRoute({ children, ...rest }) {
return (
<Route
{...rest}
render={({ location }) =>
fakeAuth.isAuthenticated ? (
children
) : (
<Redirect
to={{
pathname: '/login',
state: { from: location },
}}
/>
)
}
/>
);
}
function PublicPage() {
return <h3>Public</h3>;
}
function ProtectedPage() {
return <h3>Protected</h3>;
}
function LoginPage() {
let history = useHistory();
let location = useLocation();
let { from } = location.state || { from: { pathname: '/' } };
let login = () => {
fakeAuth.authenticate(() => {
history.replace(from);
});
};
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={login}>Log in</button>
</div>
);
}
The reason it's not updating is because it doesn't know to update. You change the route but AuthButton doesn't know to re-render based on the route you need to pass it a prop so that it knows when to update. I refactored your code to incorporate using react hooks. By using hooks you can store isAuthenticated in local state in AuthExample via useState.
From AuthExample, pass down the state value for isAuthenticated as a prop to AuthButton. If the prop changes, AuthButton will detect it and this will trigger a re-render of AuthButton and reflect the correct component structure you are looking for. See below.
import React, { useState } from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
useLocation
} from "react-router-dom";
export default function AuthExample() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const fakeAuth = {
isAuthenticated: isAuthenticated,
authenticate(cb) {
fakeAuth.isAuthenticated = true;
setIsAuthenticated(true);
setTimeout(cb, 100); // fake async
},
signout(cb) {
setIsAuthenticated(false);
fakeAuth.isAuthenticated = false;
setTimeout(cb, 100);
}
};
return (
<Router>
<div>
<AuthButton fakeAuth={fakeAuth} isAuthenticated={isAuthenticated} />
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Switch>
<Route path="/public">
<PublicPage />
</Route>
<Route path="/login">
<LoginPage fakeAuth={fakeAuth} />
</Route>
<PrivateRoute path="/protected" fakeAuth={fakeAuth}>
<ProtectedPage />
</PrivateRoute>
</Switch>
</div>
</Router>
);
}
function AuthButton(props) {
const { fakeAuth, isAuthenticated } = props;
let history = useHistory();
return isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
);
}
function PrivateRoute({ children, ...rest }) {
const { fakeAuth } = rest;
return (
<Route
{...rest}
render={({ location }) =>
fakeAuth.isAuthenticated ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)
}
/>
);
}
function PublicPage() {
return <h3>Public</h3>;
}
function ProtectedPage() {
return <h3>Protected</h3>;
}
function LoginPage(props) {
const { fakeAuth } = props;
let history = useHistory();
let location = useLocation();
let { from } = location.state || { from: { pathname: "/" } };
let login = () => {
fakeAuth.authenticate(() => {
history.replace(from);
});
};
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={login}>Log in</button>
</div>
);
}
You can also see a working example in this code sandbox. There are a few ways to do this but hooks make it easy to manipulate state values to update functional components without having to make them class components. This way also keeps most of your code intact as is just adding a few checks for when isAuthenticated is updated.
I think the problem is in rendering process.
In my opinion, if you put the sub-functions in to the exported function, this problem may solve.
If the problem won't solve, try the class base component for handling this rendering process.
wish you success

PrivateRoute component for authentification

i am developping a Reactjs-nodejs application. I would like to make a JWT authentification. when we log in, i give a unique token to the user. Then, thanks to this token, if it is valid, i allow the user to navigate through my router. my private route component is like :
PrivateRoute
My function getId is like that:
async function getId(){
let res = await axios('_/api/users/me',{config}).catch(err => { console.log(err)});
return res+1;
}
Finally the config component is the token stored in the localStorage :
const config = {
headers: { Authorization: ${window.localStorage.getItem("token")} }
};
GetId() returns the id of the user if logged in, else it is null.
The problem now is that my privateRoute always redirect to "/" path. I think it is because of the axios(promise) that gives me the userId too late. please tell me if you understand well and if you have a solution.
Thanks you
You can create private-route like this .
const PrivateRoute = ({ component: Component, ...props }) => {
return (
<Route
{...props}
render={innerProps =>
localStorage.getItem("Token") ? (
<Component {...innerProps} />
) : (
<Redirect
to={{
pathname: "/",
state: { from: props.location }
}}
/>
)
}
/>
);
};
then you can use
<PrivateRoute path="/" component={FIlname} />
if you are using redux, you can make the following:
in reduer:
case LOGIN_ADMIN_SUCCESS:
localStorage.setItem("token", action.payload.token);
return {
...state,
user: action.payload,
isSignedIn: true,
loadingSignIn: false,
token: action.payload.token,
};
then in your private router you have to check these values, like
const PrivateRoute = ({ component: Component, admin, ...rest }) => (
<Route
{...rest}
render={(props) => {
if (admin.loadingSignIn) {
return <h3>Loading...</h3>;
} else if (!admin.isSignedIn) {
return <Redirect to="/login" />;
} else {
return <Component {...props} />;
}
}}
/>
);

React - render CSS animation onClick

I'm new to React, sorry if this is too basic.
I am trying to render a simple animation when <Link> is clicked in React.
I have Coffees.jsx:
import Brewing from './Brewing.jsx';
handleClick() {
return (<Brewing/>)
}
render(){
return (
<div>
<div>
<Link onClick={this.handleClick} to="/brewing">Coffee</Link>
</div>
</div>
);
}
}
export default Menus;
and Brewing.jsx:
import './css/mug.css'
class Brewing extends Component {
constructor (props) {
super(props);
};
render() {
return (
<div>
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
</div>
);
}
}
export default Brewing;
The above is not working. It only works if I inject the animation:
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
directly into Coffees.jxs, like so:
render(){
return (
<div>
<div>
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
<Link to="/brewing"></Link>
</div>
</div>
);
}
But this is not desired...How do I render this animation at onClick?
EDIT:
App.jsx
class App extends Component {
constructor() {
super();
this.state = {
users: [],
isAuthenticated: false,
messageName: null,
messageType: null,
select:'',
email: '',
id: '',
username: '',
active: '',
admin: '',
//task:''
};
this.logoutUser = this.logoutUser.bind(this);
this.loginUser = this.loginUser.bind(this);
this.createMessage = this.createMessage.bind(this);
this.removeMessage = this.removeMessage.bind(this);
this.userId = this.userId.bind(this);
};
componentWillMount() {
if (window.localStorage.getItem('authToken')) {
this.setState({ isAuthenticated: true });
};
};
componentDidMount() {
this.getUsers();
this.userId();
};
getUsers() {
axios.get(`${process.env.REACT_APP_WEB_SERVICE_URL}/users`)
.then((res) => { this.setState({ users: res.data.data.users }); })
.catch((err) => { });
};
logoutUser() {
window.localStorage.clear();
this.setState({ isAuthenticated: false });
};
loginUser(token) {
window.localStorage.setItem('authToken', token);
this.setState({ isAuthenticated: true });
this.getUsers();
this.createMessage('Welcome', 'success');
};
userId(event) {
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/auth/status`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.data)
this.setState({
select: res.data.data.select,
email: res.data.data.email,
id: res.data.data.id,
username: res.data.data.username,
active: String(res.data.data.active),
admin: String(res.data.data.admin),
})
})
.catch((error) => { console.log(error); });
};
createMessage(name='Sanity Check', type='success') {
this.setState({
messageName: name,
messageType: type
});
setTimeout(() => {
this.removeMessage();
}, 3000);
};
removeMessage() {
this.setState({
messageName: null,
messageType: null
});
};
render() {
return (
<div>
<NavBar
title={this.state.title}
isAuthenticated={this.state.isAuthenticated}
/>
<section className="section">
<div className="container">
{this.state.messageName && this.state.messageType &&
<Message
messageName={this.state.messageName}
messageType={this.state.messageType}
removeMessage={this.removeMessage}
/>
}
<div className="columns">
<div className="column is-half">
<br/>
<Switch>
<Route exact path='/about' component={About}/>
<Route exact path='/register' render={() => (
<Form
formType={'Register'}
isAuthenticated={this.state.isAuthenticated}
loginUser={this.loginUser}
createMessage={this.createMessage}
userId={this.state.id}
/>
)} />
<Route exact path='/login' render={() => (
<Form
formType={'Login'}
isAuthenticated={this.state.isAuthenticated}
loginUser={this.loginUser}
createMessage={this.createMessage}
userId={this.state.id}
/>
)} />
<Route exact path='/logout' render={() => (
<Logout
logoutUser={this.logoutUser}
isAuthenticated={this.state.isAuthenticated}
/>
)} />
<Route exact path='/status' render={() => (
<UserStatus
isAuthenticated={this.state.isAuthenticated}
/>
)} />
<Route exact path='/seeds' render={() => (
<Seeds
isAuthenticated={this.state.isAuthenticated}
userId={this.state.id}
/>
)} />
<Route exact path='/menus' render={() => (
<Menus
isAuthenticated={this.state.isAuthenticated}
userId={this.state.id}
/>
)} />
<Route exact path='/coffee' render={() => (
<Coffees
isAuthenticated={this.state.isAuthenticated}
userId={this.state.select}
/>
)} />
</Switch>
</div>
</div>
</div>
</section>
</div>
)
}
};
export default App;
When you click any Link it will not wait on that component to execute any event, it will simply redirect to given path (to="/coffees"), so you need Route to handle this path.
Instead of Link we can use a button or simply a div (you can style it so that it look like link) and write onClick handler on that. In that handler we need to add a setTimeout with the timeout of actual animation.
Now when setTimeout executes, we can set a variable in state which will help us to redirect to desired component.
Your menu component should be,
class Menu extends React.Component{
constructor(props){
super(props);
this.state = {
isLoading: false,
redirect: false
}
}
gotoCoffee = () => {
this.setState({isLoading:true})
setTimeout(()=>{
this.setState({isLoading:false,redirect:true})
},5000) //Replace this time with your animation time
}
renderCoffee = () => {
if (this.state.redirect) {
return <Redirect to='/coffees' />
}
}
render(){
return(
<div>
{this.state.isLoading && <Brewing />}
{this.renderCoffee()}
<div onClick={this.gotoCoffee} style={{textDecoration:'underline',color:'blue',cursor:'pointer'}}>Go to Coffee</div>
</div>
)
}
}
I have used Redirect from react-router-dom package for navigation.
Demo
There are a few things to consider with your Menus component;
returning the <Brewing> JSX from handleClick() won't affect the rendering result of the Menus component, meaning that the animation in <Brewing> won't show as required
you'll need to track some state to determine if the <Link> has been clicked by the user at which point, your Menus component can render the <Brewing> component (ie that contains the animation)
One way to approach that in code would be to make the following changes in Coffee.jsx:
import Brewing from './Brewing.jsx';
class Menus extends React.Component {
constructor(props) {
super(props);
/*
Set inital state to false, which means Brewing wont initially show
*/
this.state = { hasBeenClicked : false };
}
}
handleClick() {
/*
Set hasBeenClicked state of Menus to true after click which will
cause Brewing to be rendered
*/
this.setState({ hasBeenClicked : true });
}
render(){
return (
<div>
<div>
{ /*
This is short hand for "if hasBeenClicked = true, then
render Brewing here
*/ }
{ (this.state.hasBeenClicked === true) && <Brewing/> }
<Link onClick={this.handleClick} to="/brewing">Coffee</Link>
</div>
</div>
);
}
}
export default Menus;

check if data has been loaded before passing as a prop react/redux

I have an async/await api call that is made and the data is passed into my productsReducer, the problem I'm having is that the data is not loaded into the redux state when it is passed a prop into ProductsList and therefore throws an error when it tries to filter/map the data.
adding a check to see if it is loaded is possible in ProductsList products && products but I want to check on the Main component first if it is loaded and if so only pass this then as a prop.
I have attempted to do so with products: state.data.products.loadng === false && state.data.products.items.data but this always returns false
How can i check if my data is loaded before passing it as a prop?
Main.js
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import { TransitionGroup, CSSTransition } from "react-transition-group";
import { connect } from "react-redux";
import styled from 'styled-components'
import ScrollToTop from '../atoms/ScrollToTop'
import Header from '../Header'
import Footer from '../Footer'
import Home from '../Home'
import * as actionCreators from './actions'
import media from '../atoms/Media'
import BreadCrumbs from '../Breadcrumbs'
import Category from '../Category'
import ProductsList from '../ProductsList'
import Product from '../Product'
import Alert from '../atoms/Alert'
import { fetchProducts } from "./actions";
import { ADD_TO_CART_E, ADD_TO_CART_P } from '../atoms/translations'
class Main extends React.Component {
componentDidMount() {
this.props.fetchCategories()
this.props.fetchSubCategories()
this.props.fetchProducts()
}
handleHideAlert() {
setTimeout(() => {
this.props.hideAlert()
}, 1000)
}
render() {
const {alert, categories, filteredColors, filteredSizes, language, products, showAlert, subCategories} = this.props
const e = language === 'english'
const p = language === 'polish'
return(
<Router>
<Wrap>
{alert && <div><Alert />{this.handleHideAlert()}</div>}
<Header e={e} p={p} categories={categories} subCategories={subCategories} language={language} />
{/* <BreadCrumbs /> */}
<Route style={{ flex: 1 }} render={({ location }) =>
<TransitionGroup>
<CSSTransition
key={location.key}
timeout={500}
classNames="page"
mountOnEnter={true}
unmountOnExit={true}
>
<Switch location={location}>
<MainWrap>
<Route exact path="/" render={props => <Home e={e} p={p} categories={categories} subCategories={subCategories} products={products} language={language} {...props} />} />
<Route exact path="/:catId" render={props => <Category e={e} p={p} categories={categories} subCategories={subCategories} language={language} {...props} />} />
<Route exact path="/:catId/:subCatId" render={props => <ProductsList e={e} p={p} filteredColors={filteredColors} filteredSizes={filteredSizes} categories={categories} subCategories={subCategories} products={products} language={language} {...props} />} />
<Route exact path="/:catId/:subCatId/:productId" render={props => <Product categories={categories} subCategories={subCategories} products={products} showAlert={showAlert} language={language} {...props} />} />
</MainWrap>
</Switch>
</CSSTransition>
</TransitionGroup>
} />
{console.log('products',products)}
{ e ? ADD_TO_CART_E : ADD_TO_CART_P}
<Footer />
</Wrap>
</Router>
)
}
}
const mapStateToProps = state => ({
alert: state.ui.alert,
language: state.language,
categories: state.data.categories.categories,
subCategories: state.data.subCategories.subCategories,
products: state.data.products.loadng === false && state.data.products.items.data
productsLoading: state.data.products.loadng,
filteredColors: state.filters.colors,
filteredSizes: state.filters.sizes
});
export default connect(mapStateToProps, actionCreators)(Main);
productsReducer.js
import {
FETCH_PRODUCTS_REQUEST,
FETCH_PRODUCTS_SUCCESS,
FETCH_PRODUCTS_FAILURE
} from '../../Constants'
const initialState = {
items: [],
loading: false,
error: null
};
export default function productReducer(state = initialState, action) {
switch(action.type) {
case FETCH_PRODUCTS_REQUEST:
return {
...state,
loading: true,
error: null
};
case FETCH_PRODUCTS_SUCCESS:
return {
...state,
loading: false,
items: action.payload
};
case FETCH_PRODUCTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
items: []
};
default:
return state;
}
}
productsList.js
class ProductsList extends React.Component {
render() {
const { e, p, filteredColors, filteredSizes, match, products } = this.props
const productFilter = products && products.filter(products =>
(
(filteredColors.length >= 1 && filteredSizes.length < 1 && products.cat === match.params.subCatId) && filteredColors.includes(products.color) ||
(filteredSizes.length >= 1 && filteredColors.length < 1 && products.cat === match.params.subCatId) && filteredSizes.includes(products.size) ||
(filteredSizes.length >= 1 && filteredColors.length >= 1 && products.cat === match.params.subCatId) && filteredColors.includes(products.color) && filteredSizes.includes(products.size)) ||
(filteredSizes.length < 1 && filteredColors.length < 1 && products.cat === match.params.subCatId)
)
return(
<Section>
<Container>
<Grid>
{console.log(productFilter)}
{productFilter && productFilter.map(filteredProduct =>
<Cell key={filteredProduct.id}>
<ProductListCard e={e} p={p} match={match} {...filteredProduct} />
</Cell>
)}
</Grid>
</Container>
<Filters>
<Filter />
</Filters>
</Section>
)
}
}
const mapStateToProps = state => ({
filteredProducts: state.filteredProducts
});
export default connect(mapStateToProps, actionCreators)(ProductsList);
You could set the initial state as loading is true. In the constructor of the component or in the reducer. In the render function of Main.js check if the loading state is true and make sure to render something like a loading component.
When the data is loading set the loading state to false. The page will rerender and render the list with the data. Is this enough or would you like an example?

Categories

Resources