How to get stored data from store.getState() - reactjs

I have using react with redux for the first time.
In my app's render, if I console log store.getState() I can see the stored data {username: 'foo', password: 'bar'}
but when I want to get the data like store.getState().password
I get undefined error. I am trying to pass this data to
my components for private route as:
inside PrivateRoute I then try to check if the user is logged in or not to send to dashboard
so, how do I get data to pass to my props?
even const { isLoggedIn, username } = store.getState() doesn't work, it shows
Property isLoggedIn does not exist on type {}
btw I know this might be bad but it's my first react app so I am trying to learn redux

if you are calling the store from the react application you have to use provider and pass the store to the react app, and then bind state, actions and/or methods of the store to the react props as shown in this link
connect https://react-redux.js.org/5.x/api/connect
but if you are using redux in normal javascript then it will work fine.
example in react
first
import { Provider } from 'react-redux'
import { store } from "store";
ReactDOM.render(
<Provider store={store}>
<YourReactApp/> // e.g <Container />
</Provider>, document.getElementById('root')
);
then in you can bind anything from your store to react component like this
import { connect } from "react-redux";
const mapStateToProps = (state) => {
const { isLoggedIn, username }= state
return {
isLoggedIn,
username
};
};
const mapDispatchToProps = (dispatch :any) => {
return {
login: ()=> {
return dispatch(your action creator)
}
}
}
const Containter = connect(mapStateToProps,mapDispatchToProps)(AnyReactComponentYouWantToPassThisStore);
export default Containter;
the you can use it in your page like this
function AnyReactComponentYouWantToPassThisStore (props){
return(
<div> {props.username} </div>
)
}
then instead of calling <AnyReactComponentYouWantToPassThisStore />
now use <Container />

Related

Using Redux Provider vs Redux Wrapper with NextJs | State Persists Without Wrapper

According to the docs for the next-redux-wrapper:
When Next.js static site generator or server side rendering is involved, however, things start to get complicated as another store instance is needed on the server to render Redux-connected components.
I don't understand exactly where the complication arises.
SSR to SSG Redux State Persists During Page Navigation
I built a simple NextJs app that has one page that renders on the server, and another that is statically rendered. In production mode or in development mode, I can navigate between these two pages, and my redux state remains in-tact.
My understanding is that this isn't supposed to be the case, since a separate redux store must be created on the server and on the client; hence the creation of the next redux wrapper (also for injecting redux state into NextJs lifecycle methods, which I haven't needed).
I'm confused as to what mechanism is being used to persist the redux state between SSR and SSG pages. I'm using a simple redux provider, with a simple bit of state for testing.
Note: State persists between these two page components even without using redux-persist.
Redux Persist Also Works...
I have confirmed that redux persist also works without the next redux wrapper, saving to the local storage without issue, persisting successfully on page refresh.
Here's example code of my configuration:
page1.js (Statically rendered)
import Layout from './layout';
import { useSelector, useDispatch } from 'react-redux';
import { getTest, addBar } from '../redux/slices/topten';
export default function Page1() {
const dispatch = useDispatch();
const myState = useSelector(getTest);
return (
<Layout>
<h2>Statically Rendered</h2>
<div>
<span>
{ myState }
</span>
<button onClick={() => dispatch(addBar())}>
Add Bar to Foo
</button>
</div>
</Layout>
)
}
page2.js (Server-side rendered)
import Layout from './layout';
import { useSelector, useDispatch } from 'react-redux';
import { getTest, addBar } from '../redux/slices/topten';
export default function Page2() {
const dispatch = useDispatch();
const myState = useSelector(getTest);
return (
<Layout>
<h2>Server Side Rendered</h2>
<div>
<span>
{ myState }
</span>
<button onClick={() => dispatch(addBar())}>
Add Bar to Foo
</button>
</div>
</Layout>
)
}
export async function getServerSideProps() {
// Code for fetching data from external api.
return {
props: {
// Returned props
}
};
};
App.js
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from '../redux/store';
export default function MyApp({ Component, pageProps }) {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Component {...pageProps} />
</PersistGate>
</Provider>
)
}
topten.js (This is the redux slice with a simple reducer)
const initialState = {
topTen: {
test: "foo"
}
}
const topTenSlice = createSlice({
name: 'topten',
initialState,
reducers: {
addBar: (state) => {
state.topTen.test = "foobar";
}
},
});
// Export action.
export const { addBar } = topTenSlice.actions;
// Export slice reducer.
export default topTenSlice.reducer;
// Export state.
export const getTest = state => state.topten.topTen.test;
I left out the code for Layout and store, because it's redundant. It works without the wrapper... That's what I'm trying to understand.
I have verified here that state persists between the two components simply using the Provider HOC. According to the docs:
store is being replaced on navigation
I have also verified that state is persisting on page refresh using redux-persist.
I don't understand why I would need the wrapper unless I needed access to redux state within the NextJs lifecycle methods... Which can easily be avoided using redux useSelector hooks.
Any clarity on this would be appreciated.

How to pass state to React JS High Order Component

I am using OIDC redux connector for user state. I have a few components that require authentication. I would like to use something like export default connect(mapStateToProps, mapDispatchToProps)(withAuth(Component)); and request data from state inside my authentication service.
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
export const withAuth = (Component) => {
return props => {
return <Component {...props} />
}
}
Is it possible to get state in the render function? So I can check the user beinig logged in and redirect to the sign-in page if there is no user signed in?
BTW: How would I redirect? I have tried using redirect-router-dom and <Redirect /> But then it complains about set state being changed too often ... But that might be my mistake. I get this error when I render a Redirect: Error: Maximum update depth exceeded.
If I understand correctly you want to "decorate" your component with additional logic to handle an authentication redirect?
I suggest using a "decorator" pattern here e.g.:
export const withAuth = (Component) => {
const mapStateToProps = (state, ownProps) => ({
authenticated: state.authenticated // Or what you need to do to determine this
});
return connect(mapStateToProps)(class extends React.Component {
render() {
const { authenticated, ...componentProps } = props;
if (authenticated) {
return <Component {...componentProps }>;
}
return <Redirect to="/login" />;
}
});
}
Then when you need this you can do things like:
export default withAuth(connect(yourOwnMapStateToProps)(YourComponent))
Just figured it out, I changed the store so instead of returning a function, it returns the object. So I can load in all js files. It might not be the best solution. If there is a better way to get the store in code, I would love to hear about how to do that. The configurestore function is what I found in quite a lot of examples.
import { store } from '../configureStore';
Using store.getState() I can get the current state.
The redirect issue I am having is similar to: How to use react-transition-group with react-router-dom

React-Router v4 and Redux authentication

I'm using react-router v4 and Redux for a new project.
I have the following code:
export class App extends Component {
componentDidMount() {
const { dispatch } = this.props;
dispatch(initAuth());
}
render() {
const { user } = this.props;
return (
<BrowserRouter>
<div>
<NavContainer />
<Match pattern="/login" component={LogInForm} />
<MatchWhenAuthorized pattern='/users' component={Users} user={user} />
</div>
</BrowserRouter>
);
}
}
Where initAuth dispatches an action that checks if there's an existing token in localStorage and if there is, a logIn action is dispatched as well.
The problem is that if I go directly to myapp.com/users the action hasn't returned yet, so there's no user logged in and in that case MatchWhenAuthorized redirects me to my LogInForm, which I don't want if my initAuth logs a user in.
Is there an elegant way to solve this?
I think I could solve it by rendering the MatchWhenAuthorized component only if there's a user logged in, but that doesn't seem right.
The initial login state should be set when the page is loaded and before you mount your app. I'm not sure why the initAuth is a redux action creator when you could just check the localStorage without involving redux.
index.js
import { createStore } from 'redux'
import reducer from './reducer'
import { getUser } from './storage'
// load the user data from localStorage and
// use the value in the store's initial data
const store = createStore(reducer, {
user: getUser()
})
Then you can connect your <MatchWhenAuthorized> component to the store to access the user value and redirect if there is no user in the store.

React-intl multi language app: changing languages and translations storage

I have react-router app and would like to add i18n. In react-intl example root component wrapped in IntlProvider:
ReactDOM.render(
<IntlProvider locale="en">
<App />
</IntlProvider>,
document.getElementById('container')
);
But there is only one locale. How to update app for adding other languages and how is the best way to store translations?
I have faced the same problem and this is what I found out:
To change language I used solution provided here, which is basically binding IntlProvider to ReduxStore with Connect function. Also don't forget to include key to re-render components on language change. This is basically all the code:
This is ConnectedIntlProvider.js, just binds default IntlProvider(notice the key prop that is missing in original comment on github)
import { connect } from 'react-redux';
import { IntlProvider } from 'react-intl';
// This function will map the current redux state to the props for the component that it is "connected" to.
// When the state of the redux store changes, this function will be called, if the props that come out of
// this function are different, then the component that is wrapped is re-rendered.
function mapStateToProps(state) {
const { lang, messages } = state.locales;
return { locale: lang, key: lang, messages };
}
export default connect(mapStateToProps)(IntlProvider);
And then in your entry point file:
// index.js (your top-level file)
import ConnectedIntlProvider from 'ConnectedIntlProvider';
const store = applyMiddleware(thunkMiddleware)(createStore)(reducers);
ReactDOM.render((
<Provider store={store}>
<ConnectedIntlProvider>
<Router history={createHistory()}>{routes}</Router>
</ConnectedIntlProvider>
</Provider>
), document.getElementById( APP_DOM_CONTAINER ));
Next thing to do is to just implement reducer for managing locale and make action creators to change languages on demand.
As for the best way to store translations - I found many discussions on this topic and situation seems to be quite confused, honestly I am quite baffled that makers of react-intl focus so much on date and number formats and forget about translation. So, I don't know the absolutely correct way to handle it, but this is what I do:
Create folder "locales" and inside create bunch of files like "en.js", "fi.js", "ru.js", etc. Basically all languages you work with.
In every file export json object with translations like this:
export const ENGLISH_STATE = {
lang: 'en',
messages: {
'app.header.title': 'Awesome site',
'app.header.subtitle': 'check it out',
'app.header.about': 'About',
'app.header.services': 'services',
'app.header.shipping': 'Shipping & Payment',
}
}
Other files have exact same structure but with translated strings inside.
Then in reducer that is responsible for language change import all the states from these files and load them into redux store as soon as action to change language is dispatched. Your component created in previous step will propagate changes to IntlProvider and new locale will take place. Output it on page using <FormattedMessage> or intl.formatMessage({id: 'app.header.title'})}, read more on that at their github wiki.
They have some DefineMessages function there, but honestly I couldn't find any good information how to use it, basically you can forget about it and be OK.
With a new Context API I believe it's not required to use redux now:
IntlContext.jsx
import React from "react";
import { IntlProvider, addLocaleData } from "react-intl";
import en from "react-intl/locale-data/en";
import de from "react-intl/locale-data/de";
const deTranslation = {
//...
};
const enTranslation = {
//...
};
addLocaleData([...en, ...de]);
const Context = React.createContext();
class IntlProviderWrapper extends React.Component {
constructor(...args) {
super(...args);
this.switchToEnglish = () =>
this.setState({ locale: "en", messages: enTranslation });
this.switchToDeutsch = () =>
this.setState({ locale: "de", messages: deTranslation });
// pass everything in state to avoid creating object inside render method (like explained in the documentation)
this.state = {
locale: "en",
messages: enTranslation,
switchToEnglish: this.switchToEnglish,
switchToDeutsch: this.switchToDeutsch
};
}
render() {
const { children } = this.props;
const { locale, messages } = this.state;
return (
<Context.Provider value={this.state}>
<IntlProvider
key={locale}
locale={locale}
messages={messages}
defaultLocale="en"
>
{children}
</IntlProvider>
</Context.Provider>
);
}
}
export { IntlProviderWrapper, Context as IntlContext };
Main App.jsx component:
import { Provider } from "react-redux";
import { IntlProviderWrapper } from "./IntlContext";
class App extends Component {
render() {
return (
<Provider store={store}>
<IntlProviderWrapper>
...
</IntlProviderWrapper>
</Provider>
);
}
}
LanguageSwitch.jsx
import React from "react";
import { IntlContext } from "./IntlContext";
const LanguageSwitch = () => (
<IntlContext.Consumer>
{({ switchToEnglish, switchToDeutsch }) => (
<React.Fragment>
<button onClick={switchToEnglish}>
English
</button>
<button onClick={switchToDeutsch}>
Deutsch
</button>
</React.Fragment>
)}
</IntlContext.Consumer>
);
// with hooks:
const LanguageSwitch2 = () => {
const { switchToEnglish, switchToDeutsch } = React.useContext(IntlContext);
return (
<>
<button onClick={switchToEnglish}>English</button>
<button onClick={switchToDeutsch}>Deutsch</button>
</>
);
};
export default LanguageSwitch;
I've created a repository that demonstrates this idea.
And also codesandbox example.

How to dispatch actions from Child components three level down?

I am currently facing this issue designing a React application and I don't seem to be able to find an answer to it.
So my application has following heirarchy of Components in React Router
App
-> DynamicContainer
-> -> LoginComponent
Now, LoginComponents has form elements to take username and password.
I have userActionCreators where the login is handled, and it dispatches login successful when finished, but I don't seem to be able find the right way to connect my LoginComponent to dispatch actions or call actionCreators.
How do I do it? Any suggestion would be appreciated.
One option is to bind your single-purpose forms to their actions with connect. Since <LoginComponent /> is typically always doing the exact same thing, you can use it like this:
import React from 'react';
import * as userActionCreators from '../actions/users';
import { connect } from 'react-redux';
export class LoginComponent extends React.Component {
static propTypes = {
login: React.PropTypes.func.isRequired
}
render() {
const { login } = this.props;
const { username, password } = this.state;
return (
<form onSubmit={ () => login(username, password) }>
...
</form>
);
}
}
export default connect(null, userActionCreators)(LoginComponent);
connect automatically binds the action creator and separately provides dispatch to props, so if you want to be more explicit, the above example is the same as
import React from 'react';
import { login } from '../actions/users';
import { connect } from 'react-redux';
export class LoginComponent extends React.Component {
static propTypes = {
dispatch: React.PropTypes.func.isRequired
}
render() {
const { login, dispatch } = this.props;
const { username, password } = this.state;
return (
<form onSubmit={ () => dispatch(login(username, password)) }>
...
</form>
);
}
}
export default connect()(LoginComponent);
And for reference, userActionCreators:
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
const LOGIN_FAILED = 'LOGIN_FAILED';
export function login(username, password) {
if (username === 'realUser' && password === 'secretPassword') {
return { type: LOGIN_SUCCESS, payload: { name: 'Joe', username: 'realUser' } };
} else {
return { type: LOGIN_FAILED, error: 'Invalid username or password };
}
}
if I understood you correctly, if you read Example: Todo List | Redux you'll find the example that you might be looking for.
There's the App component, connect()ed to Redux, and then there're the other components: AddTodo, TodoList, Todo and Footer.
App calls TodoList that calls Todo where user can click something. This click will surf back callback after callback, from Todo to TodoList to App as detailed below:
App calls TodoList with
<TodoList todos={visibleTodos} onTodoClick={ index => dispatch(completeTodo(index)) } />
TodoList calls Todo with
<Todo {...todo} key={index} onClick={ () => this.props.onTodoClick(index) } />
Todo component has a <li> with onClick={this.props.onClick} property.
So, backwards, when someones clicks inside the Todo compoment, that will call this.props.onClick which will call this.props.onTodoClick(index) from TodoList (notice the optional added parameter index), then, at last, this will invoke the function dispatch(completeTodo(index)) from App.
Two options:
Pass a bound actionCreator from your Container (which is connected to Redux) down to a child component (which is not connected to Redux) via the props object.
Consider adopting React Component Context in your project.

Resources