Redux, React-Redux accessing variable from one page to next - javascript

I'm defining a variable in Page1 and would like to access it in Page2 and then when clicking back to Page1 retrieve the same variable
So far, the variable is set on Page1 but cannot be retrieved on Page2
index.js
import {createStore, applyMiddleware, combineReducers} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'
import {Provider} from 'react-redux'
import variableReducer from './reducers'
const store = createStore(
variableReducer,
composeWithDevTools(applyMiddleware(thunk))
)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
serviceWorker.unregister();
actions/index.js
export const SET_MY_VARIABLE = 'SET_MY_VARIABLE'
export const setMyVariable = myVariable => ({
type: SET_MY_VARIABLE,
payload: {myVariable}
})
reducers/index.js
import {SET_MY_VARIABLE} from '../actions'
const initialState = {
myVariable: ''
}
const variableReducer = (state=initialState, action) => {
switch (action.type) {
case SET_MY_VARIABLE:
return {
...state,
myVariable: action.payload.myVariable
}
default:
return state
}
}
export default variableReducer
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Setting variable<br />
Go to page 2
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)
components/Page2.js
import React from 'react'
import {connect} from 'react-redux'
const Page2 = (props) => {
const {myVariable} = props
console.log('props: ', props)
return (
<div>
Variable: {myVariable}
</div>
)
}
const mapState = state => {
console.log('map2 ', state.myVariable)
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page2)
I should be able to set variables to the store in one component and access them throughout the entire App. Instead, I'm not able to retrieve them

Instead of using action.payload.myVariable use action.payload in your reducer/index.js

I've discovered the answer to my problem. I needed to change the <a href tag to a <Link> from react-router-dom in the Page1 component. The <a href was causing a complete reload of all JS and losing state. Here's the corrected component:
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
import {Link} from 'react-router-dom'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Variable: {myVariable}<br />
<Link to="/page2">Go to page 2</Link>
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)

Related

My react app is not re-rendering on Redux's store update

I am learning Redux, in this app I am using react-redux and redux, I am not mutating the store's state, but still my app is not re-rendering
I have this basic counter app, you press the + button the number increases, you press the - button it decreases
My code:
app.js :
`
import './App.css';
import { useDispatch } from 'react-redux';
import { store } from './store';
function App() {
const dispatch = useDispatch();
const handleIncrease = () => {
console.log("+");
dispatch({
type : 'aumentar'
})
console.log(store.getState());
}
const handleDecrease = () => {
console.log("-");
dispatch({
type : 'restar'
})
}
return (
<div className="App">
<h1>Contador</h1>
<h3>{store.getState()}</h3>
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
</div>
);
}
export default App;
`
index.js :
`
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
`
store.js
`
import { legacy_createStore as createStore } from "redux";
let initialState = 0;
const reducer = (state = initialState, action) => {
switch(action.type){
case 'aumentar' :
return state + 1;
case 'restar' :
return state - 1;
default :
return state;
}
}
export const store = createStore(reducer);
`
You need to use useSelector to access entries from the state:
// 1. import the hook
import { useDispatch, useSelector } from 'react-redux';
import { store } from './store';
function App() {
const dispatch = useDispatch();
// 2. use it to extract what you need
const count = useSelector(state => state);
const handleIncrease = () => {
console.log("+");
dispatch({
type : 'aumentar'
})
}
const handleDecrease = () => {
console.log("-");
dispatch({
type : 'restar'
})
}
// 3. use the extracted variable inside JSX
return (
<div className="App">
<h1>Contador</h1>
<h3>{count}</h3>
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
</div>
);
}
When your state will become more complex / you will use more reducers, your code will look like:
const whatYouNeed = useSelector(state => state.reducerName.something);

React Redux prop is undefined

Beginner question.
I want to pass a user object to a component from store as a prop, but the component doesn't get it (undefined). I get the user object from a third party service authentication service (google firebase)
The middleware actually logs out in the console that the action of type SET_CURRENT_USER takes place, and next state indeed will have a user.currentUser set to the object returned from the login service (NOT UNDEFINED).
However, the component doesn't re-render and doesn't seem to receive the object as prop
The component, in which the prop is undefined
import React from 'react';
import { connect } from 'react-redux';
import { auth } from "../../firebase/firebase.utils";
export const Navbar = ({ currentUser }) => {
return (
/* A LOT OF JSX CODE. currentUser IS UNDEFINED */
);
};
const mapStateToProps = state => ({
currentUser: state.user.currentUser
});
export default connect(mapStateToProps)(Navbar);
The App component, which has the above component as a child. Also, I'm trying to set the store to contain the user object in the componentDidMount()
import React from 'react';
import Homepage from "./pages/homepage";
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import Login from "./pages/login";
import Register from "./pages/register";
import { Navbar } from "./components/navbar/navbar";
import { auth } from "./firebase/firebase.utils";
import { setCurrentUser } from "./redux/user/user.actions";
class App extends React.Component {
unsubscribeFromAuth = null;
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if(userAuth) {
(async () => {
const rawResponse = await fetch(/* JUST AN ASYNC FUNCTION TO POST TO BACKEND*/);
})();
}
this.props.setCurrentUser(userAuth); /*HERE IM TRYING TO SET THE STORE*/
})
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return (
<div>
<Navbar /> /* THE COMPONENT WHICH SHOULD GET THE USER OBJECT AS PROP */
<Switch>
<Route exact={true} path={'/register'} component={Register} />
<Route exact={true} path={'/login'} component={Login} />
<Route path={'/'} component={Homepage} />
</Switch>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => ({
setCurrentUser: user => dispatch(setCurrentUser(user))
});
export default connect(null, mapDispatchToProps)(App);
The index component
import React from 'react';
import ReactDOM from 'react-dom';
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import store from "./redux/store";
ReactDOM.render(
<Provider store={store} > /* HERE IS STORE PROVIDED FROM IMPORT*/
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
Root reducer
import { combineReducers } from "redux";
export default combineReducers({
user: userReducer
});
User reducer
const INITIAL_STATE = {
currentUser: null
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_CURRENT_USER':
return {
...state,
currentUser: action.payload
};
default:
return state;
}
};
export default userReducer;
User action
export const setCurrentUser = user => ({
type: 'SET_CURRENT_USER',
payload: user
});
The store
import { createStore, applyMiddleware } from "redux";
import logger from 'redux-logger';
import rootReducer from './root-reducer';
const middlewares = [logger];
const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
You're doing both a named and default export for Navbar. The default export gets wrapped by the connect HOC that adds currentUser to its props. The named export does not.
You import it named like this: import { Navbar } from. Instead use the default export: import Navbar from.
Then I would suggest removing the named export to avoid future confusion.

How can I maintain my store's state while using react-router and redux?

I am building an app prototype that essentially simulates ecommerce. I have components that each have different items that can be added to a cart(below I just show an example of how one would basically work). These components are accessed via different routes using the react-router. There is a header component that displays the number of items currently in the cart. The header gets the number of items in the cart from the state in the redux store. However, if I navigate to a new route, the store goes back to the default state. I need the the store to keep its state when a new route is navigated to. For example, if I go to the ShoppingPage, add an item to the cart, and then go back to the Home page, I need the cart to still have an item in it.
actions.js
export const actionTypes = Object.freeze({
UPDATE_CART: Symbol('UPDATE_CART'),
});
export const updateCart = (payload) => {
return {
type: actionTypes.UPDATE_CART,
payload,
};
};
export default actionTypes;
reducer.js
import actions from './actions';
export const INITIAL_STATE = {
cart: [],
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case actions.UPDATE_CART: {
return {
...state,
cart: action.payload,
};
}
default: {
return state;
}
};
};
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer, { INITIAL_STATE } from './reducer';
const store = createStore(reducer, INITIAL_STATE);
console.log(store.getState());
ReactDOM.render(
<Provider store ={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
serviceWorker.unregister();
ShoppingPage.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { updateCart } from './actions';
class ShoppingPage extends Component {
addToCart = () => {
const cart = [...this.props.cart];
cart.push('new item');
this.props.modifyCart(cart);
render() {
return(
<div>
<button onClick={addToCart}>
Add To Cart
</button>
</div>
)
}
}
const mapDispatchToProps = dispatch => ({
modifyCart: payload => dispatch(updateCart(payload)),
});
const mapStateToProps = state => ({
cart: state.cart,
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ShoppingPage);
Home.js
import React, { Component } from 'react';
import { ListGroup, ListGroupItem } from 'reactstrap';
class Home extends Component {
render() {
return(
<div>
<ListGroup>
<ListGroupItem><a href='/ShoppingPage'>ShoppingPage</a></ListGroupItem>
</div>
)
}
}
export default Home;
Header.js
import React, { Component } from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import { connect } from 'react-redux';
class Header extends Component {
render() {
return(
<Navbar sticky='top' className='nav'>
<NavbarBrand href='/'>Online Shopping</NavbarBrand>
<span>{'Items in Cart: '}{this.props.cart.length}</span>
</Navbar>
)
}
}
const mapStateToProps = state => ({
cart: state.cart,
});
export default connect(
mapStateToProps
)(Header);
Routes.js
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './Home';
import ShoppingPage from './ShoppingPage';
const Routes = () => (
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/ShoppingPage' component={ShoppingPage} />
</Switch>
);
export default Routes;
App.js
import React from 'react';
import Routes from './Routes';
import Header from './Header';
function App() {
return (
<div>
<Header />
<Routes />
</div>
);
}
export default App;
What's likely happening is that during navigation the web app "reloads" again (which is wiping the redux state). In order to navigate with react router you want to look at <Link>.
For example,
Home.js
<a href='/ShoppingPage'>ShoppingPage</a>
should be changed to:
<Link to="/ShoppingPage">ShoppingPage</Link>

React-native-Redux: could not find stored in either the context or props of connect(componentName)

I'm getting below error even i had defined store for root component.
click to enlarge image
Not sure why im getting this error even after defining store.
index.js (rootpage)
import React, {Component} from 'react';
import {initStore} from './redux/store';
import {Provider} from 'react-redux';
import App from './App.container';
const store = initStore();
class BuddApp extends Component {
render () {
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
export default BuddApp;
This is app.container.js which is inside app.
app/app.container.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Router from './routes';
import Proptypes from 'prop-types';
import {addNavigationHelpers} from 'react-navigation';
class App extends Component {
render () {
const {dispatch, nav, userPreferences} = this.props;
console.log(this.props)
return (
<Router screenProps={userPreferences} navigation={addNavigationHelpers({dispatch, state: nav})}/>
</Provider>
);
}
}
App.propTypes = {
dispatch: Proptypes.func,
nav: Proptypes.object,
userPreferences: Proptypes.object
};
const mapStateToProps = ({nav, userPreferences}) => ({
nav,
userPreferences
});
const mapDispatchToProps = (dispatch) => ({
dispatch
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
app/pages/login.page.js
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import Login from '../components/Login/Login.component';
import {NavigationActions} from 'react-navigation';
class LoginPage extends Component {
onLoginPress(){
console.log("fsdfds")
this.props.navigation.navigate('setupProfile')
}
render () {
const {state} = this.props;
return (
<Login onLoginPress={this.onLoginPress.bind(this)} />
);
}
}
LoginPage.propTypes = {
onLoginPress: PropTypes.func
};
const mapStateToProps = (state) => ({
state:state
});
const mapDispatchToProps = (dispatch) => ({
saveLoginDetails: dispatch(addProfile(f))
});
export default connect(mapStateToProps,mapDispatchToProps)(LoginPage);
This results in getting the above mentioned error. as you can see I am passing the store in the same manner as shown in the redux example.
Am i defined store in wrong file?
Try this way:
import { createStore, applyMiddleware } from "redux"
const store = createStore(
<<Your combined reducers comes here>>,
applyMiddleware(<<All your middleware comes here>>))

reducer switch statements not working

I've just started out with Redux and trying to implement a simple MERN App (for practice).
Everything in my code is working fine, but my reducer function is showing unexpected behaviour. When an action (which gets fetches data from express api) is called my reducer correctly goes to the particular switch case data logs successfully but then three times the default case is passed and data on my component which I log is showing null. Please Help.
Here's my code:-
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import articlesReducer from './store/reducers/articlesReducer';
import registerServiceWorker from './registerServiceWorker';
const rootReducer = combineReducers({
articles: articlesReducer
});
const store = createStore(rootReducer, applyMiddleware(thunk));
const app = (
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>
);
ReactDOM.render(app, document.getElementById('root'));
registerServiceWorker();
App.js
import React, {Component} from 'react';
import {Switch, Route} from 'react-router-dom';
import './App.css';
import Home from './components/Home/Home';
class App extends Component {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={Home} />
</Switch>
</div>
);
}
}
export default App;
articles.js
export const getAllArticles = () => {
return dispatch => {
return (
fetch('http://localhost:5000/api/articles')
.then(res => res.json())
.then(data => {
dispatch({type: 'GET_ALL_ARTICLES', articles: data})
})
);
};
};
articlesReducer.js
const initialState = {
articles:null
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'GET_ALL_ARTICLES':
console.log('in reducer', action.type, action.articles[0]);
return {
...state,
articles: action.articles
};
default:
console.log('In default');
return state;
}
};
export default reducer;
myComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getAllArticles } from '../../store/actions/articles.js';
class MainPage extends Component {
componentWillMount() {
this.props.initArticles();
console.log(this.props.articles);
}
render() {
return (
<div className="container">
<br />
<h1>Here comes the articles!!</h1>
</div>
);
}
}
const mapStateToProps = state => {
return {
articles: state.articles
};
};
const mapDispatchToProps = dispatch => {
return {
initArticles: () => dispatch(getAllArticles())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MainPage);
The output in my console is somewhat like this:-
In default
In default
In default
{articles: null}
in reducer GET_ALL_ARTICLES {articles[0] Object}
I don't know what is the mistake. Thanks for help in advance.
I'm not sure whether this is actually the problem but you incorrectly access the articles. You have a root reducer with articles reducer:
const rootReducer = combineReducers({
articles: articlesReducer
});
which initial state is:
const initialState = {
articles:null
};
And in your mapDispatchToProps you "import" whole reducer state:
const mapStateToProps = state => {
return {
articles: state.articles
};
};
I think you wanted to access articles property
const mapStateToProps = state => {
return {
articles: state.articles.articles
};
};
Other than that everything seems to be fine. I would however as pointed in comment initialize articles as empty array [].
const initialState = {
articles: []
};

Categories

Resources