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

4

25

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

Womera answered 22/8, 2014 at 19:48 Comment(1)
Can you paste some of the code you're referring to?Ellette
S
43

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.

Shawnna answered 24/8, 2014 at 0:17 Comment(0)
T
7

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)

Teshatesla answered 23/8, 2014 at 21:54 Comment(6)
Hi, What bothers me is the alternative they offer (forceUpdate()) which I agree doesn't sound like the default they suggest, but is a valid option which means I can pass in props and just user forceUpdate().Womera
using forceUpdate is for using the store data directly, not by props or statesTeshatesla
@Teshatesla It could be for use with props if the store is passed in via a prop instead of referenced directly via a singleton. I actually prefer passing the store in this way (dependency injection pattern) as it allows for using different stores in different situations, like server-side rendering (where we may not want to pull in all of the client-side dispatcher/action/constants/etc stuff).Sylphid
So far I haven't found a real difference in using setState(newState) vs forceUpdate() inside a state change callback from the dispatcher. I guess in theory the state could get out of sync with setState, but I've never had that happen and I think it looks a bit cleaner. Like I said, either way seems to work just as well.Sylphid
@Sylphid 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!Mobilize
The contexts are documented now: facebook.github.io/react/docs/context.htmlAmbert
A
5

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.

Auscultation answered 25/8, 2014 at 8:35 Comment(0)
S
1

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

Substance answered 6/2, 2016 at 21:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.