-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Chaining / combining ready-made connect functions? #522
Comments
Can you provide a more concrete example? Also, take a look at the codebase in the |
Actually, soon after I wrote this it occurred to me composition can be made trivial by applying the HoC factory returned by all these connects to each-other. E.g. let ConnectedSomeFormComponent = connectForm(formName)(connectDataFetch(dataFetchMapper)(connect(regularMapStateToProps, regularMapDispatchToProps)(SomeFormComponent))); Sure it looks a bit ugly but I think that should work OK? And there is no real need for the custom connects to support the same |
I've used successfully import {flow} from "lodash/fp";
let ConnectedSomeFormComponent = flow(
connectForm(formName),
connectDataFetch(dataFetchMapper),
connect(regularMapStateToProps, regularMapDispatchToProps)
)(SomeFormComponent); |
Re concrete example, here is the "trivial" implementation of import {connect} from 'react-redux';
import updateFieldValue from './updateFieldValue';
import commitFieldValue from './commitFieldValue';
import fieldWasValidated from './fieldWasValidated';
export default function connectForm(formName) {
let mapStateToProps = (typeof formName === 'function'
? ({forms}, props) => {
let formName = formName(props);
let fieldData = formName in forms ? forms[formName] : {};
return {formName, fieldData};
}
: ({forms}) => ({
formName: formName,
fieldData: formName in forms ? forms[formName] : {}
})
);
return connect(
mapStateToProps,
{
onUpdateFieldValue: updateFieldValue,
onCommitFieldValue: commitFieldValue,
onFieldWasValidated: fieldWasValidated,
}
);
}
// used like this
let ConnectedEditForm= connectForm(
({editObj}) => editObj ? `editForm.${editObj .id}` : `createNewObj`
)(EditForm); ... except, as I said above, it actually could be composed, like so: let ConnectedEditForm= connectForm(
({editObj}) => editObj ? `editForm.${editObj .id}` : `createNewObj`
)(connect(regularMapStateToProps, ...etc)(EditForm)); |
FYI, you can use the const combinedHOC = compose(
connectForm(formName),
connectDataFetch(dataMapper),
connect(mapState, mapDispatch)
);
export default combinedHOC(MyComponent); |
Ahhhhhhhh - thank you @markerikson, it never occurred to me that it had applications for HoCs as well, not just middlewares :) |
Amusingly, @epeli pointed it out at the exact same time I did, just right before your "concrete example" comment :) |
I was typing it too and then your answers came in. ;) |
Right, thank you all :) Out of curiosity, given this seems to be a good and popular solution, what is the use case for the split up |
Okay, people, we have GOT to stop simul-posting... :) |
For me the most important thing is that it allows to write custom |
Coming to this a bit late, but here is my question. I understand the compose approach, and this works in many cases, but what if I don't want to write three separate functions that return bits and pieces of the props. How would one combine these wrapping functions in a way that takes a single function to compute the props? Let me give a concrete example, say I want to combine
Not only do I need to write three separate functions |
@j18ter : because some components may want to subscribe to the store state and not dispatch actions, or vice versa. So, by specifying those separately, we can ensure that a component that only wants to dispatch actions never has to subscribe to the store state updates, thus improving performance. |
Thank you for responding, @markerikson. Yes, this makes sense, passing null or undefined as the first argument of |
@j18ter : as a simplified explanation, we don't combine them because we're trying to avoid having end users deliberately or accidentally re-recreate functions every time the store state changes. If you absolutely insist on doing so, then you can use the If you need further logic handling, then the "right" place to do it is in a separate thunk function. |
@markerikson : Thanks again. I hadn't realized that |
Thanks again for helping me better understand the indended use of the Using a slighly different API style I created several wrapping functions for different purposes instead of a single function where one may need to pass
Then there are two wrapping functions for components that depend on either Redux state or Meteor data, but not both:
And finally the interesting one, for components that depend on both Redux state and Meteor data, and where the server-side data to be retrieved via Meter's publish/subscribe push API may depend on the state:
This seems to work, but in some cases the |
So, it turns out it is very handy and useful to just provide ready-made connect functions for some common use cases.
For example, our data fetching and caching subsystem provides it's own version of
connect()
that works exactly like the react-reduxconnect
, except it can look at the output ofmapStateToProps
and if the values are instances of some special data-fetching-definition objects the same subsystem provides, it knows how to setup the initial data fetch and subsequent updates by passing throughdispatch
frommapDispatchToProps
tomergeProps
and so forth.The problem is in order to do this we had to look at and re-implement connect's internal handling of
mapStateToProps
,mapDispatchToProps
andmergeProps
. This is brittle - we have to rely on keeping the re-implemented behavior in sync with you, even though this is a simple matter so far.The problem actually gets worse the more of these ready-made connect functions we want to implement. There is a relatively simple
connectForm
I wish to add to easily hook-up forms to our form handling subsystem. It just maps a well known area of the redux state (looking up forms by name) to a form'sfield
prop, and does a straight-forward map of form-related action creators to their respective event props. This should be trivial, but isn't, again, because we have to re-implement all that default functionality and carefully merge in the handful of things we want to do.And worst of all the system breaks down entirely when trying to combine the two (e.g. a form that also needs to fetch its data) - it simply cannot be done.
The approach I have in mind is to pass in the connect function the custom-connects call internally as a 5th argument, but this feels like it's slowly getting out of hand.
Surely there can be a more well thought out composition mechanism provided by react-redux itself? I have some thoughts on how that could look but before I go down that road I'm wondering if there is existing work towards this, or if I'm missing a much more straight-forward way of solving these kinds of problems.
The text was updated successfully, but these errors were encountered: