I am interested, how do ReactJS components interact with each other, when used in couple with Redux?
From what I've seen in the todoApp tutorial, I created a diagram how I understand that interaction (this may be totally incorrect and this is why am I asking this question):
On this diagram one can see five basic entities: Provider, Store, Container, MainComponent and Component.
For simplicity, let's call all this system an application, which, by fact, consists of one compound component, MainComponent and its children.
Provider is a component, encapsulating both Store and Container instances.
Container is an enhanced version of MainComponent, powered with Redux' dispatch() function and containing rafinated (filtered) data from the currentState of the Store instance.
Store is just a holder of currentState, the actual application' state.
MainComponent is, as described before, the central component of an application, made of Component instances (optionally).
Now, there are four Redux functions on this diagram, namely connect, selector, reducer and dispatch.
selector filters the state, giving the data which is required by MainComponent and throwing away everything else.
connect takes selector function and MainComponent class and provides the new, Container class, which then is used in Provider component to fill the MainComponent with the data.
reducer takes the current application' state and a dispatched action (which has an aim to change the current state) and gives a new, changed application' state.
dispatch is the function, joining all those previous ones together. This one "changes" the state of the MainComponent. It takes an action, then hits Store for the current application' state. The fetched state and action are passed to the reducer function, which gives the new application state. This new state is stored as the currentState inside the Store and then is passed to the selector function, which filters the state, taking only what is needed by MainComponent and then updates the props for MainComponent and makes it to re-render itself.
So is this simplified data flow/interaction diagram correct? Or do I have some fundamental misunderstanding?
Thanks!
Related
I have observed that the getDerivedStateFromProps of the React Component is called in at least 2 cases:
when the props are changed, e.g. the component imports Redux store data via component's props when the Redux store have received new data from the external API (either as a result from the request initiated by the component or as a push from external event)
when the component's setState is called, e.g. user can update data in some element of the component and this update is handled by the procedure that calls setState.
it is also called during the navigation, when props.match.params.... are changed.
So - I would like determine the cause (1. or 2.) of the getDerivedStateFromProps and make distinct actions in each case. How it is possible to make such distinction. Maybe this is not possible to do. Or maybe my architecture is flawed if I wish to do this?
The main problem for me is - how to pass business object from the redux store (to which the props of component points to) into this.state of some component for further local manipulation?
My previous attempt is documented in the question ReactJS setState conflicts with getDerivedStateFromProps in which the more concrete code examples are available.
I'm making a web app where the user draws bounding boxes around objects in an image.
How I'm using React's local state: I store the properties of the rectangle while the user is still drawing (i.e. during mouseMove before mouseUp).
How I'm using Redux's global store: Once mouseUp fires, the drawing finishes, and updates to the rectangle are no longer made. I want to 'commit' this rectangle to Redux.
Is this the right and/or canonical way to handle state management?
I also want to pass both the React and Redux states (i.e. both the rectangle being drawn right now as well as the rectangles that have been 'committed' already in Redux) as props to a child React component.
Is this possible with react-redux's mapStateToProps? I can't seem to find a way to combine React state and Redux state together in mapStateToProps.
Just to elaborate a little more on AranS's answer and give you a direct correlation to your use case:
The way you're doing it is fine in principle. You're keeping the very frequent state updates local (basically, mousemove while mousedown). It would be annoying and potentially inefficient to update the Redux state for every mouse move (ex: if using Redux logger, you'd needlessly get a mile-long log). You would only do so (as hinted at by AranS) if other components need to know the state of your mouse position in real time. I am using the same approach you are in a 3D modeling app and it works great.
To save the shape's data in Redux, you would use standard Redux methodology. If you need to brush up, I would highly recommend googling for Redux' creator, Dan Abramov, as has published some useful and often free material (look at this series for example). mapStateToProps is typically used with mapDispatchToProps via react-redux's connect method. Giving you something like:
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
mapStateToProps is used to connect redux state with a component's props. You are basically doing two separate things:
pass drawing details to the child component using the parent state:
//Parent component render function
render() {
<ChildComponent rectangle={this.state.rectangle}/>
}
pass the redux state using mapStateToProps
Have you considered saving the drawing state in redux instead of the parent component state? This way, multiple components (such as the child component) are aware of the rectangle drawing.
P.S - you can use mapStateToProps for both react and redux state but this is redundant as you have already passed react props explicitly.
mapStateToProps = (state, ownProps) => {
fromReduxStore: state.whatever,
fromReactState: ownProps //doesn't make sense as this prop exists already on the child component
}
I am using redux, react, react-router and reselect. There is a screen that contains two components, the Header and the Body. There is a component in the Header to select an ID. When a new ID is selected a new action is dispatched to update the ID in the state in redux.
Based on that ID there should be an asynchronous API call to load the element from the server and store it in the state, which should then trigger a render of the screen and display the element in the Body. The loaded element would then be extracted from the state with reselect.
The real-life scenario is a bit more complex than that, because there are many different Body components with different API calls and only one Header component. But they can be treated as different screens. So, for example:
Screen 1:
components: Header, BodyTypeA; API to call: apiA
Screen 2:
components: Header, BodyTypeB; API to call: apiB
My understanding is that the API call should be done in the reducer when it stores the new ID in the state. But how to determine which screen is being shown and which API to call if the action is dispatched by a component shared across screens? Can I somehow subscribe in the Body to changes to the ID and trigger the API call from within the screen? The screen type is determined by the react-router. Would the reducer have any access to the router to determine which screen is being shown? Are there any best practices for handling such a scenario?
Seems like you may be overthinking this. So, one thing at a time:
My understanding is that the API call should be done in the reducer when it stores the new ID in the state.
Unless you'll be using redux-loop that allows returning an action from a reducer you're wrong. In principal reducers CAN NOT dispatch any actions, they only consume them. It's the middleware layer (or action creator) where you dispatch your actions and may call your APIs.
But how to determine which screen is being shown and which API to call if the action is dispatched by a component shared across screens?
To determine "where you are" it's best to keep routing information in your state tree. Since you're using react router anyway consider using https://github.com/reactjs/react-router-redux to expose route information in your state tree and then access it from your middleware / action creators / selectors.
Can I somehow subscribe in the Body to changes to the ID and trigger the API call from within the screen?
Yes, technically you can subscribe to store changes, but normally you wouldn't have too - see my other points and hopefully it's clear enough.
The screen type is determined by the react-router. Would the reducer have any access to the router to determine which screen is being shown?
No (in general) reducers only have access to their slice of state, so while your reducer may react to react router actions (if you'll use react-router-redux - see above) to store route details you require, but in principle it should be up to your selectors and/or connected components to construct / pull data required for given component from the state tree.
Are there any best practices for handling such a scenario?
I guess use react-router-redux, introduce a middleware that will trigger an API call in response to your action and will dispatch consecutive action containing data returned from the API. That action you'll handle in your reducer, storing data in the tree.
And a random tip, if you have components like bodyTypeA, bodyTypeB then those components can (should even) keep information if they're A or B and propagate that to the action creator. E.g. if you have an action creator called requestForId(ID) you'd modify signature to be requestForId(ID, type) and in your component / callback (whenever you're invoking that action creator) you'd be passing not only the ID but also the type, i.e. bodyTypeA would call it with requestForId(ID, 'A').
I'm fairly new to React and I'm trying to understand a clean way for child components to communicate with each other.
In a simple component, I know that I can make use of props to pass data to child and callbacks for children to pass data back to parent component.
In a slightly more complex case, when I have multiple children components in a parent component, the communication between the children gets a little confusing. I'm not sure what I should do for children components of the same level to communicate with each other.
In my case, I decided that, maybe, I could use states. So I will have a state value in the parent component, and pass it on the children's props. Similarly, the callback handlers (called from the children component) in the parent component will help to set the states accordingly so that a state value gets passed on from one child to another through React's binding.
And a pseudo code could look something like:
//Inside Parent Component
constructor() {
//initialise state for the child components
this.setState({testList: []});
}
render() {
return (
<div>
<ChildA onSomething={this.onSomethingHandler} testList={this.state.testList} />
<ChildB onSomethingElse={this.onSomethingElseHandler} testList={this.state.testList} />
</div>
);
}
onSomethingHandler(evt):void {
if(blah.blah.blah) this.setState({testList: this.state.testList.splice().push(evt.value)};
}
onSomethingElseHandler(evt):void {
//Some other complex biz logic...
if(blah.blah.blah) this.setState({testList: this.state.testList.splice().push(somethingOtherStuffDueToLogic)};
}
//Inside ChildA Component
export IChildAProps {
onSomething: (evt)=>void
}
render() {
//Do some logic from the value in testList property
if(this.state.testList == blah blah)...
return (
<button onClick={this.props.onSomething({id:321, value:"wassup! I'm ChildA."})}>ChildA</button>
)
}
//Inside ChildB Component
export IChildBProps {
onSomethingElse: (evt)=>void
}
render() {
//Do some logic from the value in testList property
if(this.state.testList == blah blah)...
return (
<button onClick={this.props.onSomething({id:123, value:"yo! I'm ChildB."})}>ChildB</button>
)
}
At this point, I'm starting to wonder if the logic in that 2 handler methods, namely onSomethingHandler() and onSomethingElseHandler() in the Parent component, should actually be resided inside the child components themselves? I thought of this because those logic look like something the child component should be handling on their own to serve their purpose. The parent component shouldn't do it for them or it might just grow messy. But I've no choice because of how I'm handling their communication. Apart from this, I also created a new state simply just to allow them to communicate.
So far, this is still relatively manageable. But in my own experiment, it has got to a stage where I've children component nested inside another children components that need to communicate across other children components of the same (or sometimes different) level. Using states for communication also meant that I have many states all over the place, which doesn't look like a good idea to me. And the parent components ended up with tons of messy callback handler methods to manage all that propagation of data up and down the component tree.
The situation is so messy that I can at most illustrate it as something like so:
And you can see in the above illustration, ChildB ended up having yet another state just to help passing that information between its children components.
I'm sure I'm missing something that I should know about React. The callbacks I'm having in the parent components seem a little too much just to handle data propagation. How should I really organise the communication of children components in a clean and maintainable way?
Every React programmer hits this wall at some point in time. I did too. The answer is to use Redux for state management. You have experienced how tedious it is to use React's native state.
Redux is a state management mechanism which can be used in conjunction with React. So you won't be using React's state, instead you will use Redux.
Redux provides a single store, where the state of entire application is stored. You can access the state in your components using connect() method.
But there is a caveat. Not all of the react components are connected to the Redux store. There are two types of components-
Smart/connected components: Connected to redux store
Dumb components: Dependent on connected components
The idea is to pass the state from redux store to Connected components via React's props. The connected components can directly consume state from the store. The dumb components are not directly connected to the redux store. The connected components also pass the state to the dumb components via props. So you see, React's state is bypassed altogether. Now, if you want to change the state, following events must happen-
An event is fired from the smart/dumb component.
Actions are dispatched to the store
Reducers create a new state according to the actions.
A new state will be stored in the store.
Store will return new state to the connected components via connect() through props
Dumb components will receive new state from connected components through props
What are actions and reducers?
Actions are nothing but javascript objects that describe how to change the state.
Reducer is a "pure" function which builds and returns the new state tree according to the action dispatched to the store.
Redux - http://redux.js.org/
Redux-thunk - https://github.com/gaearon/redux-thunk
Redux-saga - https://github.com/yelouafi/redux-saga
Most fashion way is using Redux.js (or flux.js) to matain your child components state.
http://redux.js.org/
If you don't like invoke third party js. You can use refs property:
https://facebook.github.io/react/docs/more-about-refs.html
We can use the react context API,
Context provides a way to pass data through the component tree without having to pass props down manually at every level
Also, note that Mark Erikson has mentioned in his blog,
Yes, the new context API is going to be great for passing down data to deeply nested components - that's exactly what it was designed for.
If you're only using Redux to avoid passing down props, context could replace Redux - but then you probably didn't need Redux in the first place.
Context also doesn't give you anything like the Redux DevTools, the ability to trace your state updates, middleware to add centralized application logic, and other powerful capabilities that Redux enables.
To handle scenarios you mentioned, context API is a good option and you don't have to use additional libraries for that.
With Relay, you create a React component as usual:
class TodoApp extends React.Component {
...
}
And then component is wrapped in a Relay container:
export default Relay.createContainer(TodoApp, {
...
});
The Relay container will fetch data using GraphQL and then update the state. This is a higher order component and this state is then passed down as props to its children.
This isn't (or doesn't appear to be) compatible with a flux implementation like Redux. Redux has a single global state object and it too has higher order components that pass props down to presentational components. So I don't see how both the Redux store and Relay containers can coexist currently.
So then how should we update state that doesn't come from the database? How is this state supposed to be managed with Relay?
While I can’t offer you an advice on using them together, technically you can definitely have several higher order components applied one after another:
class TodoApp extends React.Component {
// ...
}
TodoApp = connect(
// ...
)(TodoApp);
TodoApp = Relay.createContainer(TodoApp, {
// ...
});
export default TodoApp;
I’m not sure this makes a lot of sense, but it’s easily doable.
These things are still in discussion and the current state of Redux and Relay may not fit well together if you use a Relay container.
You can join the discussion here
I've done this as follows for a chat application:
The chat component (ChatComponent) is a dumb react component that expects all data to come as props. It also requires the redux dispatch function as a prop so it can dispatch actions when someone wants to send a new message. This is a 'private' component and is wrapped by...
ChatComponentRelay - this renders ChatComponent but is a Relay component which is also connected to the redux store. It uses one of the lifecycle methods (not the constructor) to dispatch the data received via relay into the redux store. This is the component that is used by the rest of the application, and is basically just a proxy to the underlying dumb ChatComponent. It renders ChatComponent passing in all the data in its props from the redux store, and also the redux dispatch function as well.