Skip to content
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

Instead of providing HOCs, provide container components #5

Closed
hugooliveirad opened this issue Jun 2, 2015 · 12 comments
Closed

Instead of providing HOCs, provide container components #5

hugooliveirad opened this issue Jun 2, 2015 · 12 comments

Comments

@hugooliveirad
Copy link

As in the docs, the decorator @observes could couple the counter with the counterStore, instead of giving it the freedom from receiving the counter from any data source via props.

I like flummox's FluxComponent way of doing stuff: your component is pure and a top-level component chooses how to feed it.

import { Observes } from 'redux/components';

<Observes store="counterStore">
  <Counter /* counter={counterStore.counter} */ />
</Observes>

For the @performs decorator we could have an equivalent component that wires an event of a component (e.g. onClick, onIncrement) with an action.

import { Performs } from 'redux/components';
import increment from './actions/CounterActions.js';

<Performs actions={{'onClick': increment}}>
  <IncrementButton />
</Performs>

I think we have much more power using components this way. More testable and reusable.

I'm far from being an expert on decorators and even on Flux, so please show your views and let's discuss this thread 😄

@hugooliveirad hugooliveirad changed the title Decorators instead of HoC HoC instead of Decorators Jun 2, 2015
@gaearon gaearon changed the title HoC instead of Decorators Instead of providing HOCs, provide container components Jun 2, 2015
@gaearon
Copy link
Contributor

gaearon commented Jun 2, 2015

There are some nice sides to this approach I haven't considered before.
(They might make string keys for Stores and Action Creators avoidable.)

I'll re-evaluate and let you know.
Thanks for raising this!

@gaearon
Copy link
Contributor

gaearon commented Jun 2, 2015

Again, thanks for raising this. I have a proof of concept of such implementation and it solves several other problems in a much more elegant way. 👍

@gaearon
Copy link
Contributor

gaearon commented Jun 3, 2015

Released in 0.3.0 — a complete rewrite of the React part.

Counter example now looks like this:

import React, { PropTypes } from 'react';

export default class Counter {
  static propTypes = {
    increment: PropTypes.func.isRequired,
    decrement: PropTypes.func.isRequired,
    counter: PropTypes.number.isRequired
  };

  render() {
    const { increment, decrement, counter } = this.props;
    return (
      <p>
        Clicked: {counter} times
        {' '}
        <button onClick={() => increment()}>+</button>
        {' '}
        <button onClick={() => decrement()}>-</button>
      </p>
    );
  }
}
import React, { Component } from 'react';
import { Root, Container } from 'redux';
import { increment, decrement } from './actions/CounterActions';
import counterStore from './stores/counterStore';
import Counter from './Counter';

@Root
export default class App extends Component {
  render() {
    return (
      <Container stores={counterStore}
                 actions={{ increment, decrement }}>
        {props => <Counter {...props} />}
      </Container>
    );
  }
}

Thanks a lot again for helping figure this out.

@gaearon gaearon closed this as completed Jun 3, 2015
@hugooliveirad
Copy link
Author

Glad I helped. The final implementation looks a lot better than my initial examples.

Children as function is a clever thing.

This can also help a lot when components become stateless functions.

Can't wait to play with redux, I know it's just a proof of concept but it already looks so great.

Thank you for creating this!

@hugooliveirad
Copy link
Author

Just a question about the new Root decorator: why it isn't a component too?

@ooflorent
Copy link
Contributor

I'm really sad with this change. I always disliked Flummox API because of this... Loosing the ability to enrich 3rd party components without having to create a new component is disappointing.

E.g.: inject intl settings into react-intl components.

const wrapIntl = observes('intlStore')
export const IntlDate = wrapIntl(FormattedDate)

@hugooliveirad
Copy link
Author

You have a lot of freedom to do stuff. This may help you:

const container = function(stores, actions, Child) {
  return (
    <Container stores={stores} actions={actions}>
      {props => <Child {...props} />}
    </Container>
  );
}

or return a full component class, you choose.

@gaearon
Copy link
Contributor

gaearon commented Jun 3, 2015

Yeah exactly. <Container> is just lower level API. Totally possible to reimplement old API on top of this one.

@gaearon
Copy link
Contributor

gaearon commented Jun 3, 2015

Root could (and probably should) be a component. I just hated two level nesting of functional children in the small examples.

I think the proper way to go might be to make Root a component, but also add root and container decorators that wrap them both for people who prefer decorators. I'll accept a PR doing them.

@hugooliveirad
Copy link
Author

Indeed. I'll ping you with one in a couple hours

@gaearon
Copy link
Contributor

gaearon commented Jun 3, 2015

This is out in 0.4.0. Thanks @hugobessaa for the input, @ooflorent for the implementation!

@hugooliveirad
Copy link
Author

You guys are blazing fast

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants