Guy Nesher
Guy Nesher

Reputation: 1385

React + Flux - should store data be stored in a component state, or props?

If that the flux store is a singleton that maintains the state of the data why do the components use setState and not setProps when accessing the stores? Wouldn't it just mean that I started saving the application state in two (or more) places?

Both the Flux / React documentation and Examples seem to point to setState as the preferred solution, but I've had an interesting conversation with a few colleagues at work and wondered if anyone else came across this

Edit: You can see what I'm talking about in this url: https://github.com/facebook/flux/blob/master/examples/flux-chat/js/components/ThreadSection.react.js

Notice how ThreadSection is a child component, that is fetching data directly from a store and using it as a state.

If you follow the React "way" I would have expected the state to be managed by the store - not a child component.

The solution we thought of is to fetch all stores in the top level component (as props) and pass them down to the child components as needed. But that gets rather ugly rather quickly.

We do that because setProps does not work on child components

Upvotes: 25

Views: 19075

Answers (4)

UKM
UKM

Reputation: 305

A valid answer to this question sits hidden in the comments to a previous answer:

@idolize you can also pass stores in using React contexts (a hidden, not yet officially documented feature). It's really nice because you don't have to do all that passing down the hierarchy. There are a few articles about contexts, search for it online! – Andy Jul 17 '15 at 18:41

Upvotes: 1

yarden
yarden

Reputation: 1936

According to formal documentation, a store should update the parent component's state, and pass it down via his children props:

When it receives the event from the store, it first requests the new data it needs via the stores' public getter methods. It then calls its own setState() or forceUpdate() methods, causing its render() method and the render() method of all its descendants to run.

We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage.

(facebook flux docs - Overview)

Upvotes: 7

Spoike
Spoike

Reputation: 121772

It makes more sense to put store data in the component's state, this is because props may change by a parent component with componentWillReceiveProps. So it makes sense to update the state whenever:

  • the store's change event is fired and
  • whenever the props change (putting derivative data related only to the component itself to the state)

Below is a sample component that updates listening to a reflux store and also on props change. I rarely use this.props in the render function, instead I amend them (create derivative data that is only used within the component itself) as new props come in. I constantly run into this pattern so might as well write this down:

var SampleComponent = React.createClass({
    mixins: [Reflux.ListenerMixin],

    // reusable helper function to build state object
    buildStateFromProps: function(props) {
        return {
            actualHeight: props.height + 20
        }
    },

    // default props if no such was set by a parent component
    getDefaultProps: function() {
        return {
            height: 100
        };
    },

    // initial state with all value set to something default
    // even using buildStateFromProps with default props
    getInitialState: function() {
        // this.props is built before this.state
        var state = buildStateFromProps(this.props);
        // append default data from store
        state.text = '';
    },

    // happens when the parent component send different 
    // props data
    componentWillReceiveProps: function(nextProps) {
        // building derivative data from new props
        // reusing buildStateFromProps
        this.setState(buildStateFromProps(nextProps));
    },

    // setting up store to be used by the component
    componentDidMount: function() {
        // this.listenTo is a helper function ListenerMixin
        this.listenTo(sampleStore, sampleUpdated);
    },

    // is called from the sampleStore update
    sampleUpdated: function(sampleData) {
        this.setState({
            text: sampleData.text
        });
    },

    render: function() {
        return (
            // ... 
            // using this.state.text from store updates and
            // this.state.height from prop updates
        );
    }
});

The reason I send props data to state is to avoid cluttering up the render function. Otherwise the render function will contain a lot of code that is not really related to "rendering" the component. Furthermore if this derivative data is used in other parts of the application then it is easy to pull it out from the component and put it into the store.

Hope this helps.

Upvotes: 5

user217782
user217782

Reputation:

Understand that you should have 2 kinds of components. Stateful components and view components.

Stateful components can have 3 kinds of states: initial state, user input state, and data store state.

Stateful components are like small entry points in the "widget" that you're assembling. There is no single application-wide entry point anymore for downstream dependency or data injection, because all of these widgets have their own isolated lifecycles. That's why they themselves need to access & listen to stores.

Besides behavorial properties, stateful components do not receive actual data via upstream properties.

Stateful components manage their own state and pass it to their children to render through downstream properties.

Stateful components do not normally render html DOM elements themselves directly. They're more like the controllers in MVC, and use other dumber components, the ones like views in MVC, to actually render DOM elements.

Dumber components are like views so they only contain logic to render DOM elements. Think of them as handlebars.js templates that only receive properties, and simply render those into DOM elements possibly with loops etc. They are stateless renderers.

Hope this answers your question.

Upvotes: 43

Related Questions