as far as I understood Redux it is about keeping all state of the UI in one store (to be able to reproduce certain states easily and to have no side-effects). You can manipulate the state via triggering actions which trigger reducers.
I am currently writing a small blog-like app where you can simply create and edit posts. I have a dialog for creating a post, roughly the render
method of the App
component returns something like this:
<div>
<AppBar ... />
<PostFormDialog
addPost={actions.addPost}
ref="postFormDialog" />
<PostList
posts={posts}
actions={actions} />
</div>
My question is: should the state of the dialog (opened or closed) be part of the state object of the App component? And should therefore opening and closing the dialog be triggered via an action instead of doing something like the following:
onTriggerCreatePostDialog(e) {
this.refs.postFormDialog.show();
}
where onTriggerCreatePostDialog
is triggered via some click listener on a "create" button or so.
It seems a little bit strange to me to do it via an actions because is introduces kind of an "indirection".
However, assuming that I want to reuse the dialog for the edit action, I must be able to open the dialog from elements which are deeper in the component structure, for example from a Post
component which is a child of the PostList
component. What I could do is pass the onTriggerCreatePostDialog
function down the hierarchy via the props
property, but that seems cumbersome to me...
So int the end is is also about communicating between components which are not in a direct parent-child relationship. Are there other options? Should I somehow utilise a global event bus? I am quite unsure currently.
Another way to share data between multiple components is to use a state management solution. A popular state management solution for React apps is Redux and React-Redux. In index. js , we have the rootReducer with that takes the state value and return the latest value of the store based on the action type we dispatch.
The only way to update a state inside a store is to dispatch an action and define a reducer function to perform tasks based on the given actions. Once dispatched, the action goes inside the reducer functions which performs the tasks and return the updated state to the store. This is what Redux is all about.
The way you have it written, the state won't update unless you explicitly update it using setState() (most likely in the componentWillReceiveProps() method). When you use mapStateToProps() with the Redux connect() HOC, you are mapping your Redux state to your component through its props, so in your case this. props.
dispatch(action) Dispatches an action. This is the only way to trigger a state change. The store's reducing function will be called with the current getState() result and the given action synchronously.
It seems to me that you are on the right path. The docs can be a bit tricky at first, but I can tell you how my team and I are using the implementation.
To address your first question; if the state is specific to a component then we keep that state with the component. An example of this would be a panel that pages records locally --- nothing else needs to be aware of that behavior. So in this instance we wouldn't trigger a redux action when the page changed, that would be handled internally within the component structure using refs.
Our redux state is comprised primarily of data collected via xhr requests or from a shared state. An example of shared state would be managing a time range among multiple components that use that range to display data. In this instance we would trigger a redux action; update the date state with whatever it was changed to (while also updating some other state items via xhr) and then ultimately that gets back to the components and they re-render.
With that said, triggering actions via refs is totally acceptable, it's just about what the specific use case is.
To address your second question; redux recommends using the Smart & Dumb component concept. So you are right that you would pass a function down the tree for the dumb components to utilize.
We utilize mapDispatchToProps in our connect setup. So basically you pass a function that returns an object of function "dispatchers". You will be able to access those functions directly in your smart component's this.props
.
Example of mapDispatchToProps
function mapDispatchToProps(dispatch) {
return {
myAction: () => dispatch(actions.myAction()),
};
}
So that works 99% of the time, but I've run into some corner cases where we do use a global event bus, so don't be afraid to utilize both while attempting to stick to the Smart / Dumb component method as much as possible.
As a side note, I would recommend using reselect to map your redux state to the smart component. You can also find other great redux resources here (there are several things listed that we use).
The state for the dialog should be in the redux store, triggered by actions. whether it should be rendered should be determined by checking that state in the redux store.
App.render()
should be something like this:
render() {
const { showDialog } = this.props;
return (
<div>
<AppBar ... />
{ showDialog ? <PostFormDialog ... /> : false }
<PostList ... />
</div>
);
}
where mapStateToProps
would be something like state => {{ showDialog: state.showDialog }}
In terms of delivering the action creator, passing it down the props tree is probably the correct way to do it unless you have some good location where it makes sense to have another smart component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With