Cannot connect component to redux store - javascript

In my profile page I have 3 cards, which means 3 react-components.
I am new to React.js and recently set up redux in my application. So the problem is two of them (cards) successfully connected to redux store, but third card can't for unknown reasons, can you help me please?
Redux store just returns undefined only for Card3.js
Hero.js (Profile)
import React, { useState, useEffect } from "react"
import FadeIn from 'react-fade-in';
import { Redirect } from 'react-router-dom'
import { Container, Row } from 'react-bootstrap'
import { getUserProfile } from "../../services/user.service";
import { connect } from "react-redux";
import './profile.css'
import LCard1 from "./cards/card1/LCard1"
import Card1 from "./cards/card1/Card1"
import Card2 from "./cards/card2/Card2"
import Card3 from "./cards/card3/Card3"
const Hero = (props) => {
const [userInfo, setUserInfo] = useState({username: ''})
const [isLoading, setIsLoading] = useState(true)
const getUser = async () => {
console.log()
await getUserProfile(props.uid)
.then(res => {
setUserInfo(res)
setIsLoading(false)
})
}
useEffect(() => {
if (userInfo)
getUser()
}, [])
return (
<>
<FadeIn transitionDuration={1000}>
{props.authenticated ? (
<section className="profile-cards">
<Container fluid >
<Row>
{isLoading ? (
<LCard1 />
) : (
<Card1 user={userInfo} />
)}
<Card3 />
</Row>
<Row>
<Card2 />
</Row>
</Container>
</section>
) : (
<Redirect to="/signin" />
)}
</FadeIn>
</>
)
}
const mapStateToProps = (state) => {
return {
uid: state.authReducer.uid,
authenticated: state.authReducer.authenticated
}
}
const mapDispatchToProps = (dispatch) => {
return {
logout: () => {
dispatch({
type: 'SIGNOUT_SUCCESS'
})
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Hero)
Card3.js
import React from 'react'
import { connect } from "react-redux";
import CreateAdvertisement from './content/create.advert/CreateAdvertisement'
import MyMessages from './content/my.messages/MyMessages';
import MySettings from './content/my.settings/MySettings';
import MyVerfifcation from './content/my.verif/MyVerfifcation';
import './card3.css'
const Card3 = (props) => {
return (
<div>
<p>Test: {props.username}</p>
<section className="card3">
<div className="card3-box">
<div>
<MySettings />
</div>
</div>
</section>
</div>
)
}
const mapStateToProps = (state) => {
return {
key: state.authReducer.username
}
}
export default connect(
mapStateToProps
)(Card3)
store.js
import { configureStore } from '#reduxjs/toolkit'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import thunk from 'redux-thunk'
import reducers from './reducers/root.reducer'
const persistConfig = {
key: 'root',
storage: storage,
blacklist: ['sidebarReducer']
}
const persistedReducer = persistReducer(persistConfig, reducers)
const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
middleware: [thunk]
})
export default store
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import { persistStore } from 'redux-persist'
import store from './store/store'
let persistor = persistStore(store)
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById('root')
);

import React, { Component } from 'react';
import { View, Text,ScrollView } from 'react-native';
import { Provider } from 'react-redux'
import store from './src/Redux/Store'
import MainNavigation from './src/components/Navigation/Navigation';
class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<Provider store={store}>
<MainNavigation/>
</Provider>
);
}
}
export default App;
First your all screens should under a navigation then
follow this code This will work for you Thanks.

Related

Why does createStore work but configureStore doesn't?

I am creating a simple counter app using react and redux.
The following is the counterSlice.js file.
import { createSlice } from "#reduxjs/toolkit";
export const counterSlice = createSlice({
name: "counter",
initialState: { count: 0 },
reducers: {
changeValueBy(state, action) {
const value = action.payload;
state["count"] = state["count"] + value;
}
}
});
export const { changeValueBy } = counterSlice.actions;
export const selectCount = (state) => state.count;
export default counterSlice.reducer;
The following is the app/store.js file:
import { configureStore } from "#reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
export default configureStore({
reducer: {
counter: counterReducer
}
});
The following is the index.js file:
import App from "./App";
import store from "./app/store";
import { Provider } from "react-redux"
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
With this setup, the code doesn't work.
(Entire code is in this sandbox)
But with following setup, the store works.
The App.js file:
import { Counter } from "./features/counter/Counter";
import "./App.css";
import { Provider } from "react-redux";
import { createStore } from "redux";
import counterSlice from "./features/counter/counterSlice";
const store = createStore(counterSlice);
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Provider store={store}>
<Counter />
</Provider>
</header>
</div>
);
}
The entire code is in this sandbox.
I would like to use configureStore form the #reduxjs/toolkit package and not the outdated createStore from redux package.
Any idea how I can achieve this?
export default configureStore({
reducer: {
counter: counterReducer
}
});
is equivalent to
const store = createStore(combineReducers: {
counter: counterSlice
});
which means that the data from counterSlice will be found at state.counter.
Your old code does createStore(counterSlice), which means that the data from counterSlice will be found at state.
So, both work, but you will have to select the data from different places depending on what you do.
Your selector
export const selectCount = (state) => state.count;
would have to be
export const selectCount = (state) => state.counter.count;
instead.

Redux-persist does not work with Redux-Toolkit

The app works fine The only thing is that redux persist is not working as it should.Persist doest not persist anything,it does not give any errors in the console(in VSC or in Chrome) so I have zero information about where it is failing.Any ideas? thanks for your time and sorry for my bad english
This is the App.js
import FuseAuthorization from '#fuse/core/FuseAuthorization';
import FuseLayout from '#fuse/core/FuseLayout';
import FuseTheme from '#fuse/core/FuseTheme';
import history from '#history';
import { createGenerateClassName, jssPreset, StylesProvider } from '#material-ui/core/styles';
import { MuiPickersUtilsProvider } from '#material-ui/pickers';
import { create } from 'jss';
import jssExtend from 'jss-plugin-extend';
import rtl from 'jss-rtl';
import Provider from 'react-redux/es/components/Provider';
import { Router } from 'react-router-dom';
import { SnackbarProvider } from 'notistack';
import DateFnsUtils from '#date-io/date-fns';
import AppContext from './AppContext';
import { Auth } from './auth';
import routes from './fuse-configs/routesConfig';
import {store persistor} from './store';
import { PersistGate } from 'redux-persist/integration/react';
import { persistStore } from 'redux-persist';
const jss = create({
...jssPreset(),
plugins: [...jssPreset().plugins, jssExtend(), rtl()],
insertionPoint: document.getElementById('jss-insertion-point')
});
const generateClassName = createGenerateClassName({ disableGlobal: true });
const App = () => {
return (
<AppContext.Provider
value={{
routes
}}
>
<StylesProvider jss={jss} generateClassName={generateClassName}>
<Provider store={store}>
<PersistGate persistor={persistor}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<Auth>
<Router history={history}>
<FuseAuthorization>
<FuseTheme>
<SnackbarProvider
maxSnack={5}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
classes={{
containerRoot: 'bottom-0 right-0 mb-52 md:mb-68 mr-8 lg:mr-80 z-99'
}}
>
<FuseLayout />
</SnackbarProvider>
</FuseTheme>
</FuseAuthorization>
</Router>
</Auth>
</MuiPickersUtilsProvider>
</PersistGate>
</Provider>
</StylesProvider>
</AppContext.Provider>
);
};
This is where the store is created:
import { configureStore } from '#reduxjs/toolkit';
import createReducer from './rootReducer';
import storage from 'redux-persist/lib/storage';
import { persistReducer } from 'redux-persist';
if (process.env.NODE_ENV === 'development' && module.hot) {
module.hot.accept('./rootReducer', () => {
const newRootReducer = require('./rootReducer').default;
store.replaceReducer(newRootReducer.createReducer());
});
}
const middlewares = [];
if (process.env.NODE_ENV === 'development') {
const { createLogger } = require(`redux-logger`);
const logger = createLogger({ collapsed: (getState, action, logEntry) => !logEntry.error });
middlewares.push(logger);
}
const persistConfig = {
key: 'root',
storage
};
const persistedReducer = persistReducer(persistConfig, createReducer());
export const store = configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
immutableCheck: false,
serializableCheck: false
}).concat(middlewares),
devTools: process.env.NODE_ENV === 'development'
});
store.asyncReducers = {};
export const injectReducer = (key, reducer) => {
if (store.asyncReducers[key]) {
return false;
}
store.asyncReducers[key] = reducer;
store.replaceReducer(createReducer(store.asyncReducers));
return store;
};
export const persistor = persistStore(store);
This is the createReducer():
import { combineReducers } from '#reduxjs/toolkit';
import auth from 'app/auth/store';
import fuse from './fuse';
import i18n from './i18nSlice';
const createReducer = asyncReducers => (state, action) => {
const combinedReducer = combineReducers({
auth,
fuse,
i18n,
...asyncReducers
});
/*
Reset the redux store when user logged out
*/
if (action.type === 'auth/user/userLoggedOut') {
state = undefined;
}
return combinedReducer(state, action);
};
export default createReducer;

React Native Net Info it works once

native-info package like tutorial but just enters once and initial render, I implemented App.js is my initial rendering page and I use redux and react nativagiton to App.js return method I confuse why console.warn triggered once and initial render, my App.js code like below
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {Provider} from 'react-redux';
import {PersistGate} from 'redux-persist/integration/react';
import {NavigationContainer} from '#react-navigation/native';
import { SafeAreaProvider } from "react-native-safe-area-context";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { NavigationContainer } from "#react-navigation/native";
export default function App() {
let navigator;
const [netInfo, setNetInfo] = useState("");
const [netConnection, setNetConnection] = useState(false);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
setNetInfo({
isConnected: state.isConnected,
});
});
return () => {
unsubscribe();
};
}, []);
useEffect(() => {
console.warn(netInfo.isConnected);
if (netInfo.isConnected === undefined) {
if (netInfo.isConnected) {
setNetConnection(true);
} else {
setNetConnection(false);
}
}
}, [netInfo]);
return (
<Provider store={store}>
{netConnection && <RnModal show={netConnection} />}
<PersistGate loading={null} persistor={persistor}>
<SafeAreaProvider>
<NavigationContainer
ref={(nav) => {
navigator = nav;
}}
>
<Navigator />
</NavigationContainer>
</SafeAreaProvider>
</PersistGate>
</Provider>
);
}

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

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)

React-Router v4 rendering wrong component but matching correctly

I've got a sidebar with two buttons, 'test' and 'about'. Test (rocket icon) is rendered at '/test', and About (home icon) is rendered at '/'.
They're both located at the root of the app and are nested within a component.
When I start at '/' and click the Link to="/test" it always loads the 'About' component, and when I check the props for the componentDidMount of 'About', the match object contains match data for "/test".
Only when I refresh does it render the proper component, 'Test', again. Any idea why this is happening?
AppRoutes.js:
export class AppRoutes extends React.Component {
render() {
return (
<div>
<Switch>
<Route
exact path="/"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/About')} {...matchProps} />
)}
/>
<Route
path="/login"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/Login')} {...matchProps} />
)}
/>
<Route
path="/register"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/Register')} {...matchProps} />
)}
/>
<Route
path="/test"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/Test')} {...matchProps} />
)}
/>
...
AboutPage.js && TestPage.js (duplicates except for component name):
import React from 'react';
import SidebarContainer from 'containers/SidebarContainer';
import SidebarPageLayout from 'styles/SidebarPageLayout';
export const About = (props) => {
console.log('About Loading: ', props);
return (
<SidebarPageLayout>
<SidebarContainer />
<div>About</div>
</SidebarPageLayout>
);
}
export default About;
SidebarContainer.js:
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import Sidebar from 'sidebar/Sidebar';
import HamburgerButton from 'sidebar/HamburgerButton';
import AboutButton from 'sidebar/AboutButton';
import ProfileButton from 'sidebar/ProfileButton';
import TestButton from 'sidebar/TestButton';
export class SidebarContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
sidebarIsOpen: false,
sidebarElements: [],
};
}
componentDidMount() {
if (!this.props.authenticated) {
this.setState({
sidebarElements: _.concat(this.state.sidebarElements, HamburgerButton, ProfileButton, AboutButton, TestButton),
});
}
}
toggleSidebarIsOpenState = () => {
this.setState({ sidebarIsOpen: !this.state.sidebarIsOpen });
}
render() {
const { authenticated, sidebarIsOpen, sidebarElements} = this.state;
return (
<div>
<Sidebar
authenticated={authenticated}
sidebarIsOpen={sidebarIsOpen}
sidebarElements={_.isEmpty(sidebarElements) ? undefined: sidebarElements}
toggleSidebarIsOpenState={this.toggleSidebarIsOpenState}
/>
</div>
);
}
}
SidebarContainer.propTypes = {
authenticated: PropTypes.bool,
};
export default SidebarContainer;
Sidebar.js:
import React from 'react';
import _ from 'lodash';
import PropTypes from 'prop-types'
import SidebarStyles from '../styles/SidebarStyles';
export const Sidebar = (props) => {
if (props && props.sidebarElements) {
return (
<SidebarStyles sidebarIsOpen={props.sidebarIsOpen}>
{_.map(props.sidebarElements, (value, index) => {
return React.createElement(
value,
{
key: index,
authenticated: props.authenticated,
sidebarIsOpen: props.sidebarIsOpen,
toggleSidebarIsOpenState: props.toggleSidebarIsOpenState,
},
);
})}
</SidebarStyles>
);
}
return (
<div></div>
);
}
Sidebar.propTypes = {
authenticated: PropTypes.bool,
sidebarIsOpen: PropTypes.bool,
sidebarElements: PropTypes.array,
toggleSidebarIsOpenState: PropTypes.func,
};
export default Sidebar;
TestButton.js:
import React from 'react';
import PropTypes from 'prop-types';
import Icon from 'react-fontawesome';
import {
Link
} from 'react-router-dom';
export const TestButton = (props) => {
return (
<Link to="/test">
<Icon name='rocket' size='2x' />
</Link>
);
}
export default TestButton;
AboutButton.js:
import React from 'react';
import PropTypes from 'prop-types';
import Icon from 'react-fontawesome';
import {
Link
} from 'react-router-dom';
export const AboutButton = (props) => {
return (
<Link to="/">
<Icon name='home' size='2x' />
</Link>
);
}
export default AboutButton;
No refresh, just constant clicking on the '/test' route from the '/' route:
after refresh:
Edit:
Root components:
Edit:
store.js:
import {
createStore,
applyMiddleware,
compose,
} from 'redux';
import createSagaMiddleware from 'redux-saga';
import { rootReducer } from './rootReducers';
import { rootSaga } from './rootSagas';
// sagas
const sagaMiddleware = createSagaMiddleware();
// dev-tools
const composeEnhancers = typeof window === 'object' && (
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? (
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
) : compose
);
export function configureStore() {
const middlewares = [
sagaMiddleware,
];
const store = createStore(
rootReducer,
{},
composeEnhancers(applyMiddleware(...middlewares))
);
sagaMiddleware.run(rootSaga);
return store;
}
export const store = configureStore();
index.js (root):
import React from 'react';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { store } from './store';
import AppContainer from 'containers/AppContainer';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<AppContainer />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
AppContainer:
import React from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { logout, verifyToken } from './actions';
import { selectAuthenticated, selectAuthenticating } from './selectors';
import AppRoutes from 'routes/AppRoutes';
export class AppContainer extends React.Component {
constructor(props) {
super(props);
this.state = { loaded: false };
}
componentDidMount() {
const token = localStorage.getItem('jwt');
if (token) {
this.props.verifyToken(token, () => this.setState({ loaded: true }));
} else {
this.setState({ loaded: true });
}
}
render() {
if (this.state.loaded) {
return (
<AppRoutes
authenticated={this.props.authenticated}
authenticating={this.props.authenticating}
logout={this.props.logout}
/>
);
} else {
return <div>Loading ...</div>
}
}
}
function mapStateToProps(state) {
return {
authenticated: selectAuthenticated(state),
authenticating: selectAuthenticating(state),
};
}
function mapDispatchToProps(dispatch) {
return {
verifyToken: (token = '', callback = false) => dispatch(verifyToken(token, callback)),
logout: () => dispatch(logout()),
};
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(AppContainer));
Edit 2 for LazyLoad:
services/LazyLoad/index.js:
import React from 'react';
export class LazyLoad extends React.Component {
constructor(props) {
super(props);
this.state = {
AsyncModule: null,
};
}
componentDidMount() {
this.props.getComponent() // getComponent={() => import('./someFile.js')}
.then(module => module.default)
.then(AsyncModule => this.setState({AsyncModule}))
}
render() {
const { loader, ...childProps } = this.props;
const { AsyncModule } = this.state;
if (AsyncModule) {
return <AsyncModule {...childProps} />;
}
if (loader) {
const Loader = loader;
return <Loader />;
}
return null;
}
}
export default LazyLoad;
Your problem lies with LazyLoad component. For both "/" or "test" paths, what AppRoutes component ultimately renders is a LazyLoad component. Because Route and Switch just conditionally render their children. However, React can't differentiate "/" LazyLoad component and "/test" LazyLoad component. So the first time it renders LazyLoad component and invokes the componentDidMount. But when route changes, React consider it as a prop change of previously rendered LazyLoad component. So it just invokes componentWillReceiveProps of previous LazyLoad component with new props instead of unmounting previous one and mount a new one. That's why it continuously show About component until refresh the page.
To solve this problem, if the getComponent prop has changed, we have to load the new module with new getComponent inside the componentWillReceiveProps. So we can modify the LazyLoad as follows which have a common method to load module and invoke it from both componentDidMount and componentWillReceiveProps with correct props.
import React from 'react';
export class LazyLoad extends React.Component {
constructor(props) {
super(props);
this.state = {
AsyncModule: null,
};
}
componentDidMount() {
this.load(this.props);
}
load(props){
this.setState({AsyncModule: null}
props.getComponent() // getComponent={() => import('./someFile.js')}
.then(module => module.default)
.then(AsyncModule => this.setState({AsyncModule}))
}
componentWillReceiveProps(nextProps) {
if (nextProps.getComponent !== this.props.getComponent) {
this.load(nextProps)
}
}
render() {
const { loader, ...childProps } = this.props;
const { AsyncModule } = this.state;
if (AsyncModule) {
return <AsyncModule {...childProps} />;
}
if (loader) {
const Loader = loader;
return <Loader />;
}
return null;
}
}
export default LazyLoad;

Categories

Resources