Passing props to NavBar doesn't update component - javascript

I am building an ad website. I built a registration system, which works perfectly fine, but for some reason I can't update the NavBar based on the event that has happened. For example, I want to replace the NavLink called "LOGIN/REGISTER" with "LOGGED IN". I have passed the props of the User.ID from the parent component (App.js) into the other components without any problem, but cannot do this for the NavBar. If I try a console.log - it would say undefined. I am going to put a couple of codes demonstrating where it works and where it does not:
APP.JS
*imports, which I am skipping*
const cookies = new Cookies();
class App extends Component {
constructor(){
super();
this.state = {
}
this.LogUser = this.LogUser.bind(this);
this.LogoutUser = this.LogoutUser.bind(this);
}
LogUser(User, ID){
cookies.set('User', User, { path: '/' });
cookies.set('UserID', ID,{ path: '/'});
}
LogoutUser(){
cookies.remove('User')
}
render() {
return (
<div>
<div>
//MENU <- WHERE I CAN'T PASS THE PROPS OF USER AND USERID
<Menu render={(props) => <Menu {...props} User={cookies.get('User')} ID={cookies.get('UserID')} LogOutUser={this.LogoutUser} />}/>
</div>
<Router history = {history} >
<div>
//I have removed all other routes as they are not needed, but here is an example, in which the passing of props works
<Route path = "/Profile" render={(props) => <Profile {...props} User={cookies.get('User')} ID={cookies.get('UserID')} LogOutUser={this.LogoutUser} />}/>
</div>
</Router>
</div>
);
}
}
export default App;
And for example in Profile.jsx, I can do that:
PROFILE.JSX
export default class Profile extends Component {
constructor(props, context) {
super(props, context);
this.state = {
LoggedUser: '',
UserID: '',
};
this.LogOutClick = this.LogOutClick.bind(this);
}
LogOutClick(){
this.props.LogOutUser();
history.push('/Logout');
}
componentDidMount(){
if (this.props.User !== undefined)
{
this.setState({LoggedUser: this.props.User, UserID: this.props.ID})
}
else
{
history.push('/Login');
}
}
render() {
return (
<div>
Hello, {this.props.User}!
<div>
)}}
But when I try it in the Menu component, I can't manage it to update accordingly:
NAVBAR.JSX
export default class Menu extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
isOpen: false,
Title: '',
};
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
//here I tried to put something similar to the ComponentDidMount() in Profile.jsx, but it didn't work.
componentDidMount(){
if (this.props.User !== undefined)
{
this.setState({LoggedUser: this.props.User, UserID: this.props.ID})
this.setState({Title: "LOGGED IN"})
}
else
{
this.setState({Title: "LOGIN/REGISTER"})
}
}
render() {
console.log(this.state.User)
console.log(this.state.ID)
return (
<div>
<Navbar color="light" light expand="md">
<NavbarBrand href="/"><img src={require('./images/home.png')} width = "25px" height = "25px"/></NavbarBrand>
<NavbarToggler onClick={this.toggle} />
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto1" navbar>
<NavItem>
<NavLink href="/Ads"><b>ADS</b></NavLink>
</NavItem>
<NavItem>
<NavLink href="/Profile"><b>YOUR PROFILE</b></NavLink>
</NavItem>
<NavItem>
//What I want to update
<NavLink href="/Login"><b>{this.state.Title}</b></NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>
</div>
);
}
}

React will only update in response to a new state or new props. You are manipulating a cookie which can't cause a component re-render. Here's a solution:
In your App component change the Log methods to:
constructor(){
super();
this.state ={
currentUserId: cookies.get('UserID'),
currentUser: cookies.get('User')
};
this.LogUser = this.LogUser.bind(this);
this.LogoutUser = this.LogoutUser.bind(this);
}
LogUser(User, ID){
cookies.set('User', User, { path: '/' });
cookies.set('UserID', ID,{ path: '/'});
this.setState({
currentUserId: ID,
currentUser: User
});
}
LogoutUser(){
cookies.remove('User');
this.setState({
currentUserId: null,
currentUser: null
});
}
And your render will become:
render() {
return (
<div>
<div>
<Menu render={(props) => <Menu {...props} User={this.state.currentUser} ID={this.state.currentUserId} LogOutUser={this.LogoutUser} />}/>
</div>
<Router history = {history} >
<div>
//I have removed all other routes as they are not needed, but here is an example, in which the passing of props works
<Route path = "/Profile" render={(props) => <Profile {...props} User={this.state.currentUser} ID={this.state.currentUserId} LogOutUser={this.LogoutUser} />}/>
</div>
</Router>
</div>
);
}

Related

React router renders component once after that only url changes

I am having a navbar and a side bar for my page.
Navbar consists of home and blogs
Blogs will render BlogHome Component which will fetch links from db and on click of any link will render BlogContent component.
Lets say the side bar has Blog1,Blog2 and Blog3 listed. If I click Blog1 it renders Blog1's content properly to its side, but if I click Blog2 again it just changes URL but not the Blog2's content.
Please take a look at my code:
Navbar.js
<Router>
<Container className="p-0" fluid={true}>
<Navbar className="border-bottom" bg="transparent" expand="lg">
<Navbar.Brand>{global.config.appname}</Navbar.Brand>
<Navbar.Toggle className="border-0" aria-controls="navbar-toggle" />
<Navbar.Collapse id="navbar-toggle">
<Nav className="ml-auto">
<Link className="nav-link" to="/">Home</Link>
<Link className="nav-link" to="/blogs/main">Blogs</Link>
<Link className="nav-link" to="/contact">Contact</Link>
</Nav>
</Navbar.Collapse>
</Navbar>
</Container>
<Switch>
<Route exact path="/" component={Home}></Route>
<Route exact path="/blogs/main" component={BlogHome}></Route>
</Switch>
</Router>
BlogHome.js
export default class BlogHome extends Component {
constructor(props)
{
super(props);
this.state = { data: null,route:null };
}
componentDidMount = () => {
console.log("BlogHome");
BlogDataService.getAll().then(data => {
let data_temp = []
let cnt = 0;
for (let item of data.data) {
data_temp.push(
<MenuItem key={cnt++} icon={<FaBlog />}>
<Link to={"/blogs/main/" + item.id}>{item.title}</Link>
</MenuItem>
);
}
this.setState({ data: data_temp });
})
}
render() {
return (
<Router>
<div style={{ display: "flex" }}>
<ProSidebar>
<Menu iconShape="square">
{this.state.data}
</Menu>
</ProSidebar>
<Switch>
<Route exact path={"/blogs/main/:blogId"} component={BlogContent}></Route>
</Switch>
</div>
</Router>
);
}
}
BlogContent.js
export default class BlogContent extends Component {
constructor(props) {
super(props);
const contentState = convertFromRaw(content);
this.state = {
contentState,
item_id: this.props.match.params.blogId,
title:null
}
console.log(this.props.match);
}
onContentStateChange: function = (contentState) => {
this.setState({
contentState,
});
};
componentDidMount = () => {
BlogDataService.get(this.state.item_id).then(data => {
console.log(data);
this.setState({ title: data.data.title })
});
}
render() {
const { contentState } = this.state;
return (
<Router>
<div style={{padding:"10px"}}>
<div style={{padding:"50px",fontSize:"50px"}}>
{this.state.title}
</div>
<Editor
wrapperClassName="demo-wrapper"
editorClassName="demo-editor"
onContentStateChange={this.onContentStateChange}
/>
<Route exact path={"/blogs/main/1"} component={BlogContent}></Route>
</div>
</Router>
);
}
}
Thank you for reading :)
your item_id is set only one time and it is not changing at all. On first time when component load it will work but when you are doing second time you are passing new item id but component is not aware about this change hence not able to do anything.
Try to create a function which fetch data. Same function call it in componentDidmount.
Now when it is getting new props it is time to check . Use componentDidUpdate.
componentDidUpdate(prevProps, prevState){
if(prevProps.blogId != this.props.blogId){
this.setState({
item_id: this.props.blogId
}, () => { call the function to get the data } )
}
}

Changing Nav Bar Contents on Authentication React

I am trying to change the navigation bar contents from Sign In/Register to other things such as Profile once the user logs in. My server sends a 401 when the user is not logged in and I have a HOC (RequireAuth.js) which checks the same for protected routes and redirects them to login if they have not logged in. However, I could not come up with a way to change the navbar contents with this logic and was wondering if there is a good way to do this (I do not want to use Redux for this purpose if possible).
RequireAuth.js
const RequireAuth = ( Component ) => {
return class Apps extends React.Component {
state = {
isAuthenticated: false,
isLoading: true
}
checkAuthentication = async() => {
const url = '/getinfo'
const json = await fetch(url, {method: 'GET'})
if (json.status !== 401) {
setTimeout(
function() {
this.setState({isAuthenticated: true, isLoading: false});}.bind(this), 1500);
} else {
setTimeout(
function() {
this.setState({isLoading: false});}.bind(this), 1500);
}
}
componentDidMount() {
this.checkAuthentication()
}
render() {
const style = {position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)" };
console.log(this.state.isLoading)
const { isAuthenticated, isLoading } = this.state;
if(!isAuthenticated) {
return this.state.isLoading? <div style={style}><PacmanLoader color={'#36D7CC'}/></div> : <Redirect to="/" />
}
return <Component {...this.props} />
}
}
}
export { RequireAuth }
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
const NotFoundComponent = () => <div>404 NOT FOUND</div>
return (
<div>
<Router>
<NavigationBar />
<Switch>
<Route exact path = '/'
component = {LandingPage}
/>
<Route exact path = '/register'
component = {Register}
/>
<Route exact path = '/Profile'
component = {RequireAuth(Profile)}
/>
<Route exact path = '/About'
component = {RequireAuth(About)}
/>
<Route exact path = '/Name'
component = {RequireAuth(Name)}
/>
<Route path="*" component = {NotFoundComponent}/>
</Switch>
</Router>
</div>
);
}
}
export default withRouter(App);
Navigation.js
class NavigationBar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Navbar bg="dark" variant="dark" expand="lg">
<Navbar.Brand >Hello</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto">
<Nav.Link as={Link} to='/'>Login</Nav.Link>
<Nav.Link as={Link} to='/register'>Register</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
)
}
}
export default withRouter(NavigationBar);

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;

How to pass `props` data from parent to child on react router

I want to pass my props data from parent to child when it will route to new path.
I tried some https://github.com/ReactTraining/react-router/issues/4105 but not actually worked when i passed it through {...props}.
any help would be greatful.
//App.js
class App extends Component{
constructor(props){
super(props);
}
render(){
return (
<div className="App">
<div className="navbar">
<h2 className="center">Tiny Book Library</h2>
</div>
<Switch>
<Route exact path="/" component={PostBook}/>
<Route exact path="/abc" render={props => <AllBook someProp="2" {...props} />} />
</Switch>
</div>
);
}
}
//Allbook.js
class AllBook extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>
{Object.keys(this.props.posts).length !== 0 ? <h1 className="post-heading">All books</h1> : ""} {/*To check if array is empty or not*/}
{/*Arrow function to map each added object*/}
{this.props.posts.map((post) =>(
<div key={post.id}>
{post.editing ? <EditComponent post={post} key={post.id}/> :
<Post key={post.id} post={post}/>}
</div>
))}
</div>
);
}
}
const mapStateToProps = (state) => {
return{
posts: state
}
}
export default connect(mapStateToProps)(AllBook);
//reducer
const postReducer = (state = [], action) => {
switch(action.type){
case 'ADD_BOOK':
return state.concat([action.data]);
case 'DELETE_BOOK':
return state.filter((post) => post.id !== action.id);
case 'EDIT_BOOK':
return state.map((post)=>post.id === action.id ? {...post, editing:!post.editing} : post)
case 'UPDATE':
return state.map((post)=>{
if(post.id === action.id){
return{
...post,
title: action.data.newTitle,
number:action.data.newNumber,
author:action.data.newAuthor,
description:action.data.newDescription,
editing: !post.editing
}
}
else return post;
})
default:
return state;
}
}
export default postReducer;
UPDATE #1: Adding a Link for the sake of a Correct Page Transition:
First: import Link component at the beginning of your App.js:
import { Link } from 'react-router-dom'
Second: Add Link components to route between both pages:
class App extends Component{
constructor(props){
super(props);
}
render(){
return (
<div className="App">
<div className="navbar">
<h2 className="center">Tiny Book Library</h2>
<Link to="/">Post A Book</Link>
<Link to="/abc">All Books</Link>
</div>
<Switch>
<Route exact path="/" component={PostBook}/>
<Route exact path="/abc" render={props => <AllBook someProp="2" {...props} />} />
</Switch>
</div>
);
}
}
There is probably nothing wrong with the code above, the cause of this behavior is changing the route manually from the browser.
Which causes the problem.
Explaining further:
You post the data from the form in PostBook.
The data is stored in the reducer.
You change the url manually from the browser, you lose all the data you have submitted to the reducer, this is not an error, nor an expected behavior, because doing that will request entirely new app for you, thus, ZERO DATA.
Please read about Link, to change the url properly so you can test your code whether it works or not.
As mentioned in the react-router docs, the parameters in the render function prop are the router props: i.e. the props that the Route component would normally get: history, location and more.
You don't want that, you want to pass your own/parent props:
\ App.js
class App extends Component{
constructor(props){
super(props);
}
render(){
const myProps = this.props;
return (
<div className="App">
<div className="navbar">
<h2 className="center">Tiny Book Library</h2>
</div>
<Switch>
<Route exact path="/" component={PostBook}/>
<Route exact path="/abc" render={() => <AllBook someProp="2" {...myProps} />} />
</Switch>
</div>
);
}
}
Not sure if would be helpfull.
Subscribe Allbook to posts, in your case looks like your entire redux store is just posts, or at least you are passing all redux state to posts.
class AllBook extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>
{Object.keys(this.props.posts).length !== 0 ? <h1 className="post-heading">All books</h1> : ""} {/*To check if array is empty or not*/}
{/*Arrow function to map each added object*/}
{this.props.posts.map((post) =>(
<div key={post.id}>
{post.editing ? <EditComponent post={post} key={post.id}/> :
<Post key={post.id} post={post}/>}
</div>
))}
</div>
);
}
}
// (state) => { return { posts: state.posts } }; Should also work.
const mapStateToProps = ({ posts }) => ({ posts });
export default connect(mapStateToProps)(AllBook);

Redux store not connected

I am developing a Reactjs web application from scratch and encountered a tricky situation which i need help with. Whenever i navigate away from a particular url and navigate back, my redux store does not seem to be connected.
routes.js
const RouteList = () => (
<main>
<Switch>
<Route path="/abc/" exact component={withRouter(HomePage)} />
<Route path="/abc/xyz" exact component={withRouter(XYZPage)} />
<Redirect from="/" to="/abc/" />
<Route component={Error} />
</Switch>
</main>
);
export default RouteList;
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render () {
return (
<Router history={browserHistory}>
<div>
<Header />
<RouteList />
<Footer />
</div>
</Router>
);
}
}
export default App;
Header.js
const Header = () => {
return (
<Navbar expand="md">
<NavbarBrand tag={NavLink} to="/">
<img src={brandImage} style={{marginRight: "0", width: "40px", height: "40px"}} /><strong style={{color: "#457B9D"}} >Datum</strong>
</NavbarBrand>
<Nav className="mr-auto" navbar>
<NavItem>
<NavLink className="nav-link" to={"/abc/xyz"} >XYZ</NavLink>
</NavItem>
</Nav>
</Navbar>
);
};
export default withRouter(Header);
When i hit the NavLink which will take me to url: /"abc/xyz", it will take me to XYZPage.js
XYZPage.js
class XYZPage extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
activeTab: "1"
};
this.toggle = this.toggle.bind(this);
}
toggle(tab) {
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
});
}
}
render () {
return (
<main>
<div className="container-fluid pt-3">
<Nav tabs>
<NavItem>
<NavLink
className={classnames({active: this.state.activeTab === "1"})}
onClick={() => {this.toggle("1"); }} >
AAA
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({active: this.state.activeTab === "2"})}
onClick={() => {this.toggle("2"); }} >
BBB
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({active: this.state.activeTab === "3"})}
onClick={() => {this.toggle("3"); }} >
CCC
</NavLink>
</NavItem>
</Nav>
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1">
<Row>
<AAAPAge/>
</Row>
</TabPane>
<TabPane tabId="2">
<Row>
<BBBPage/>
</Row>
</TabPane>
<TabPane tabId="3">
<Row>
<CCCPage/>
</Row>
</TabPane>
</TabContent>
</div>
</main>
);
}
}
export default withRouter(XYZPage);
Each of the AAAPage, BBBPage & CCCPage are components which needs to have some pre-populated dropdowns which i declared in my index.js below:
index.js
const store = configureStore();
store.dispatch(loadAAA());
store.dispatch(loadBBB());
store.dispatch(loadCCC());
render((
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
), document.getElementById('app'));
loadAAA, loadBBB & loadCCC are all thunks
The configureStore() method is as such:
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
composeWithDevTools(
applyMiddleware(thunk, reduxImmutableStateInvariant()),
)
);
}
To shorten this post i give a sample of my AAAPage as the others are of similar structure:
AAAPage.js:
class AAAPage extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {...};
}
componentWillReceiveProps(nextProps) {...}
render() {
[...]
return (
<Container fluid>
<Row>
<AAAInputForm
// Data from Store is passed here
/>
</Row>
{ChildComponent}
</Container>
);
}
}
AAAPage.propTypes = {
DATA: PropTypes.array
};
function mapStateToProps(state, ownProps) {
let DATA = [];
if (state.AAAReducer.length > 0) {
DATA = state.AAAReducer;
}
return {
DATA: DATA
};
}
export default withRouter(connect(mapStateToProps)(AAAPage));
AAAReducer.js:
export default function AAAReducer(state=initialState.AAAList, action) {
switch(action.type) {
case types.LOAD_AAA_SUCCESS:
return action.AAAList;
default:
return state;
}
}
AAAAction.js:
export function loadAAASuccess(AAAList) {
return {
type: types.LOAD_AAA_SUCCESS,
AAAList: AAAlList
};
}
// thunk
export function loadAAA() {
// A thunk will always return a function that accepts a dispatch
return function(dispatch) {
return apiCall("ALL").then(response => {
dispatch(loadAAASuccess(response.data.AAA));
}).catch(error => {
throw(error);
});
};
}
initialState.js:
export default {
AAAList: [],
BBBList: [],
CCCList: []
};
At this point i believe i provided enough background to my code. I followed tutorials when designing this redux store and I am not sure why when i navigate from "/abc/xyz" to "/abc" and back, or when i navigate to "/abc/xyz" from "/abc", my stores are empty although i called the loadAAA() method at my index.js. All the other pages are affected as well. However, when i hit "/abc/xyz" directly, my stores are connected and my dropdowns are populated. What is happening? Is it because of my lifecycle methods?
I am using react v15.6.2, redux v3.7.2 & redux-thunk v2.3.0.
Thanks for the guidance.
You only call loadAAA at the top level of index.js, which only executes once when your page loads. If you want to dispatch it every time your XYZPage page renders, put in XYZ's componentDidMount
#AKJ - #Andy Ray said it correctly, but I'll like to add that componentDidMount is the best place to load async calls, as it is called after render and about Store redux store keeps data until you refresh the page after refresh redux store is reinitialized, if you need store the data after refresh try redux-persist

Categories

Resources