-
Notifications
You must be signed in to change notification settings - Fork 154
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
Class-based React components and composition vs. mixins #117
base: master
Are you sure you want to change the base?
Conversation
I'll elaborate my tweet-replies here.
|
@BinaryMuse You are absolutely on the right track about deprecating mixins in favor of wrapper components (I call them "adapters", i.e. FluxxorAdapter, to avoid "controller" and other MVC parlance). You might find some inspiration below, if you haven't read these posts already: Looking forward to see what you will come up with. I would embrace ES6 for the Fluxxor store as well, i.e. have a |
@BinaryMuse is this something that you're going to add to the lib? Also, you say "I have this approach working with the React Router example in the Fluxxor repo" is it on another branch? |
87bffe0
to
f331505
Compare
@joaovpmamede This will land in Fluxxor in one form or another. I've converted this issue into a PR and attached the code I have working thus far. All the flux-based concerns are now in // This example is the most basic; `RecipeList` is wrapped by a
// component that re-renders it anytime the "recipe" store is changed,
// passing in `flux.store("recipe").getRecipes()` as its `recipes` prop.
RecipeListWrapped = wrap(RecipeList, ["recipe"], (flux) => {
return {
recipes: flux.store("recipe").getRecipes()
};
});
// This example is similar, except no stores are watched. The purpose
// of wrapping the component is to provide a flux action creator
// as one of its properties.
RecipeAdderWrapped = wrap(RecipeAdder, [], (flux) => {
return {
onAddRecipe: (name, desc, ingredients, directions) => {
flux.actions.recipes.add(name, desc, ingredients, directions);
}
};
}); And the components themselves have no knowledge of the flux system whatsoever (everything comes through props): // in RecipeAdder
onSubmit(e) {
e.preventDefault();
var newRecipe = this.refs.form.getValue();
if (newRecipe) {
this.props.onAddRecipe(
newRecipe.name,
newRecipe.description,
newRecipe.ingredients,
newRecipe.directions
);
}
} Please feel free to give it a look and let me know what you think. |
7e2be7f
to
a2e18c7
Compare
Base on this concept I made a simple version to fit our project. PR and issue are welcome. |
Changes since initial draft:
Ctx
->Context
wrapStatic
which delegates towrap
React v0.13 brought us native ES6-based React classes, which do not (currently, anyway) support mixins. This is both a blessing and a curse: a curse because Fluxxor's primary workflow depends on it, and a blessing because, overall, mixins are overused and many problems can better be solved by composition over inheritance (and multiple inheritance at that).
I've started to experiment with a composition-based approach to writing Fluxxor apps, and wanted to get an issue open early to get any feedback.
The basic idea is this:
FluxMixin
andStoreWatchMixin
into your React components and transferring Fluxxor store state to component state viagetStateFromFlux
, you would wrap your component in some kind ofFluxxorController
component.FluxxorController
is responsible for transferring state from the flux stores into props of your wrapped component.This has the additional benefit of decoupling components that might otherwise be sloppily coupled to Fluxxor (e.g. now they take props and are more reusable/testable). In order to make
FluxxorController
as simple to use as possible, I'd also like to provide a terser approach for the cases when you will follow a common pattern (which I believe will be most of the time). I have this approach working with the React Router example in the Fluxxor repo.FluxxorController
takes three properties:fluxxorStores
- an array of stores to watch and re-render on, similar toStoreWatchMixin
fluxxorProps
- A function that takes aFlux
instance and returns an object; the keys and values of this object will be passed as props to your componentfluxxorExtraContext
- An object (or function that returns an object) that will be passed as a second parameter tofluxxorProps
; useful for getting access to outside references, e.g. a router instanceHere's an example in use:
In this example, on initial render (and whenever the
recipe
store changes),FluxxorController
will re-renderRecipeList
, passing a prop calledrecipes
that has a value offlux.store("recipe").getRecipes()
.Since this is such a common pattern,
FluxxorController.wrap
can take the component, stores, props function, and extra context function and create a wrapping component for you. The above code could be rewritten:Another pattern that may prove interesting/useful is to declare this data on the React component itself.