I'm currently building a React application based on https://github.com/react-boilerplate/react-boilerplate-cra-template.
This boilerplate, uses Redux with slices. Which works fine, as long as I want to update the state from within a React component. However, I am struggling with updating my application state from somewhere else. In my particular case I want to dispatch a logout event as soon as I get a response that I am unauthorized, basically resetting the application state.
Now this obviously can happen in any API request and therefor I don't want this to handle within the React component and rather handle it in my API library that handles API requests.
From what I've read so far, I should be able to dispatch an event by simply accessing the store. In this boilerplate the store is defined in the src/index.tsx file. So after adding the export keyword to it I thought I would be able to do this:
import { store } from 'index';
import { appActions } from 'app/slice';
console.log(store.getState());
store.dispatch(appAction.logout());
Somehow the state here is always undefined, even tho there is a state when I access it via a React component. Also the reducer called above never picks up the dispatched event. I also tried to encapsulate it with a timeount, in case this is just because the store isn't initialized yet, but that's not the case.
I'd appreciate your help :)
Please try to check the path is correct to get to the store.
After that please try again. I think your method is right.
If it is not working, please try to use the require method
require('index');
require('app/slice');
console.log(store.getState());
store.dispatch(appAction.logout());
Ok, here is how I finally made it work:
I imported the React hook of my slice into the src/app/index.tsx file
import { useAppSlice } from 'app/slice';
export function App() {
// use the React hook
useAppSlice();
return (
<BrowserRouter>
...
</BrowserRouter>
);
}
Even tho I'm not using the slice in this particular controller, it somehow "connects" with the store, so that I can use the slice from within any sub-controller or any library.
Related
I have an react app using redux where every container handles its own state.
However, I have some global states that I'm having a hard time deciding how to manage. The main one is everything having to do with authentication.
I want every container in my app have access to logged-in user's info, to the access and refresh tokens, etc. I also want certain authentication operations (say, refreshing a token, or logging out) to be available for dispatching throughout the app.
I ended up created a container that deals with everything Authentication (that has its own constants, actions, reducers, selectors and sagas). As such, whenever I want to dispatch an authentication operation (say, logout) I can do it from every page, just be importing the authentication reducer and actions.
My problem is that now, for every container on the app I have 2 sets of reducers, actions, etc: Those that belong directly to container that is currently active (say, user profile), and those that belong to the global app's authentication state (see below).
I feel like this goes against the principal of Separation of Concerns, but I have pulling hairs trying to figure out how to structure this.
Since authentication is such a basic element most web-apps, I am sure there's a "right way" of doing it, but my searches are not yielding any fruits.
Can anyone help? I'd love any tip or resource that may help.
I'm not sure what you mean when you say that you're violating separation of concerns. Having multiple reducers, actions, etc. being used in the same component is totally fine. It's a scope issue. Think of it like this, in javascript, every single function has access to the global scope and its own scope (this is simplified) and React Components are simply functions. If you have something that needs to be available globally (I don't like this word as it is not actually global - it's scoped to the top component), then set it up at the top of the tree.
One nice way to do this is to create your own custom hooks that you can import when and where you need that will expose what you need where you need it. The hooks can import what they need from the reducers / actions / etc. and only expose simple calls to the components. You'll still have multiple scoped stores connected to your components, but you won't have to include them over and over in each component. Just include the hook you want that has the functionality you need for that component, and you're done.
Here's a pseudo-example...
// mycustomhook.js
import action from './myactionfile.js'
import { useSelector } from 'react-redux' // not necessary to use this lib, just easy
export default useMyHook = () => {
const myValue = useSelector(state => state.reducer.myValue)
return {
myValue,
myFunction: action
}
}
then in a component
// someComponent.js
import useMyHook from './mycustomhook.js'
export default props => {
const {myValue, myFunction} = useMyHook()
// rest of component here
}
In my React App.js component I execute the following code:
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getAuthenticatedUser());
}, []);
This code fetches the current authenticated user by session from an API and will fill the redux store. However this code seems to run after the children of App are rendered. The children components use state variables, because this function is executed after my child components are rendered it won't work. I need this function to be called once before I render my child components within App.js, how can I achieve this?
You can't ask React to delay rendering, that's just not how React is supposed to work (unless you're using experimental features like Suspense, but you should probably learn idiomatic React first).
You normally simply tell React what to display depending on the current state/props, and React will automatically rerender as soon as something changed.
So if you have no state variable saying that the app is in a loading state, you should make one. After all, whether it is currently loading or not is intuitively part of the current "state" of the app, especially if you are on a slow connection for instance.
You could just do something like a ternary that only renders those children when these variables are available, something like:
variables ? <Children/> : <Loading/>
Suspense is a great experimental feature that could help you handle this if you're willing to be an early adopter!
Say I have two components, App and DialogBox;
so my react store also has two objects
const rootReducer = combineReducers({
app: appReducer,
dialog: dialogReducer
});
export const store = createStore(rootReducer);
Now, when I have to open the Dialog box, I call the dialogBoxActions.openDialog()
And, when I close the Dialog box, I call dialogBoxActions.closeDialog() and also appActions.notifyDialogClosed();
This works, but is there a way to do this in more clearer way?
For example can I use the state.dialog from the store in App? This is what I tried in the App
const mapStateToProps = (state) => {
return {
checkCloseDialog: state.dialog.openDialog
}
}
The ComponentWillReceiveProps does get the checkCloseDialog object, but it gets the old state. I debugged to find out that it gets triggered correctly after the reducer function of the DialogBox component but I get old data.
Is there any elegant way of accessing each other's store or is it Redux's philosophy that components should communicate with each other via actions?
Ah! Okay, my bad.
Yes, it is possible. And it works. My mistake was I was trying to use it inside ComponentWillReceiveProps() method. The thing is, it seems the redux store gets updated later. So, ComponentWillReceiveProps() will hold an old state.
Hence, render() and ComponentDidUpdate() methods get the updated state.
The aim here is to help reduce the multiple action calls between independent components.
For example, a Dialog should not be concerned with what the caller should do after it closes itself. Instead the caller should subscribe to the state of Dialog to decide what to do.
Hope this helps others.
I have a project that uses React and Redux. I have multiple React components that need to use a common method that will scrape data out of the Redux Store, build a request object and fire off a rest request to the server.
I wouldn't want to write the method that does this more than one time. So, should this method be it's own React component, or should I just put it in a common javascript file? I'm not sure what the point of having a component would be if you're not rendering any JSX. On the other hand, if I put it in a common javascript file, is it possible to wire up redux to that file to get access to the states in the store?
Thanks in advance.
Adding the redux-thunk middleware lets you dispatch functions, which then get access to dispatch and getState(). From there you can do anything you want, such as using selector functions to extract pieces of state that you need, and making AJAX calls. The thunk action creators can be passed to components as props, including binding them to auto-dispatch:
function someThunk() {
return (dispatch, getState) => {
const state = getState();
// do anything you want here
}
}
const actions = {doStuffOnClick : someThunk};
export default connect(mapState, actions)(MyComponent);
Further resources:
redux-thunk is described in its repo at https://github.com/gaearon/redux-thunk .
I have a gist demonstrating some common thunk usage patterns at https://gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e.
My React/Redux links list has links to articles discussing thunks and side effects in the Redux Side Effects category.
It should not be a Component because, as you said, there is no visual aspect to it.
Create a new action that does just that and dispatch the action from anywhere you need. This action could check the store to see if the action has already happened and then do nothing (or refresh).
The module that uses import {createStore} from 'redux' creates your store. Import the created store in the action module and use store.getState() to inspect the current state of your store.
I have been using Redux for a few weeks now and I am very happy with it and I am getting used to a Redux way. I am using it with React. Still plenty to learn as both things are new to me.
I have a one problem - maybe I am doing something wrong ... Let me show you:
I have a component structure that looks like this:
App //root of the application aka smart component
CampaignTable
CampaignHeaderRow
CampaignHeader
CampaignDataRow
CampaignData
The App component is initialized as(only related code):
import * as DashboardActions from '../actions.js'
function select(state){
return {
campaigns: state.campaigns, // array of campaign objects, has name, id, time created etc
order: state.order // sort format "byWhichField"
// will affect the way how campaigns are displayed
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(DashboardActions, dispatch)
}
export default connect(select, mapDispatchToProps)(App);
App has now access to state and all actions as props.
The problem I see with it right now is:
I would like CampaignHeader to fire the action that will change the state.order state. Let say I will make <th>Text</th> inside CampaignHeader clickable. This will fire the action to change state.order which will in turn affect campaigns order on a next rerender.
So I have my action available inside App props. To pass it down to
CampaignHeader I would have to:
pass it down to CampaignHeader as props
assign it to variable inside CampaignHeader and pass it down as props to CampaignHeaderRow
assign it to variable inside CampaignHeaderRow and pass it down as props to CampaignHeader
assign it to variable inside CampaignHeader and fire the action inside onClick event....
This is a lot of boilerplate, assignments and bag passing! Just to get action fired.
All the components along the way are aware of this action.
When I decided to implement this feature I have opened CampaignHeader component file. I have added the logic and called the action, I have added the action to action file. All I needed is to get a props set. CampaignHeader component doesn't hold a reference to its parent so I didn't know straight away where should this props be injected from(in this example is obvious but I hope you get a point).
What if I will have even deeper component structure?
Is this approach correct?
Could I tackle this problem differently?
UPDATE:
As #Errorpro suggested will it be ok to connect single action and state.order to CampaignHeader?
Worried about: If I will do it once I will be doing it all the time.
There's a discussion in the issue-section of the Redux github repo about wether it's okay to use multiple connects or if everything should be passed down from the top through props, and in there Dan Abramov (the creator of Redux say's:
[...]
Nobody advocates a single connect.
[...]
The "single" only refers to small apps like the one we create in the
example. Please feel free to amend the docs to better clarify this. I
am now busy with other projects so please don't expect this issue to
get any movement unless somebody makes a PR. You can do it too.
The comment probably makes more sense in context though so check out the entire issue thread https://github.com/rackt/redux/issues/419#issuecomment-140782462
If you use redux you should know about dumb and smart component. So we use this sctructure:
component
index.js
Component.js
ComponentContainer.js
Dumb component just get props and render it. More interesting in smart component. Here it is:
export default compose(
relay({
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
any data from relay
}
`,
},
}),
connect(
null,
{
onCreate: createUserAction,
},
(stateProps, actionProps, parentProps) => ({
...parentProps,
onCreate={() => actionProps.onCreate('user')},
})
),
)(Component);
So, parentProps and onCreate function will be in dumb component's props. There you can use this.props.onCreate and invoke it or pass it farther.
Passing the actions - like any other props - from parent to child to grandchild etc is the idiomatic React way. In my opinion your approach is correct; even if it feels wrong.
But there are a couple of alternatives.
There is a feature in React called context. Context permits the passing of fields from a higher order component to a lower order component whilst skipping the middlemen. However, it's an experimental feature so I would recommend avoiding it for now. https://facebook.github.io/react/docs/context.html
Additionally, there is a Redux specific way where you can make any lower order node of your choosing a "smart component" (in the Redux sense). That is, you wrap your class export in the connect function to plug it directly to the store, in the exact same way you do for the Root node.
Personally I tend to stick to the top-down way. There may be a fair bit of boilerplate involved but at least it means your application is easy to reason about.