4
\$\begingroup\$

I'm just trying to learn the basics of React and Flux, and the above code works and I can see it works nicely.

index.js

import React from 'react'
import ReactDOM from 'react-dom'

import UI from './components/ui.js'

ReactDOM.render(<UI />, document.getElementById('ui'));

component/ui.js

import React, { Component } from 'react'
import Dispatcher from '../dispatcher/dispatcher'
import PlayerStore from '../stores/playerstore'
import PlayerActions from '../actions/playeractions'
import { ActionTypes } from '../constants/actions'

export default class UI extends Component {

    constructor(props, context) {
        super(props, context)

        // keep contexts
        this.onPlayerStoreChange = this.onPlayerStoreChange.bind(this)

        this.state = {
            health: PlayerStore.health
        }
    }

    componentDidMount() {

        // listen for events that are broadcasted by the stores
        PlayerStore.on('change', this.onPlayerStoreChange)
    }

    componentWillUnmount() {
        PlayerStore.off('change', this.onPlayerStoreChange)
    }

    onPlayerStoreChange() {

        // request the new data needed via the stores' public getter methods
        const health = PlayerStore.health

        // then call its own setState() or forceUpdate() method
        // causing its render() method and the render() method of all its descendants to run.
        this.setState({
            health
        })
    }

    healPlayer() {

        // the dispatcher exposes a method that allows us to trigger a dispatch to the stores,
        // and to include a payload of data, which we call an action
        PlayerActions.healPlayer();
    }

    render() {
        return (
            <div>
                <h2>UI</h2>
                <p>hello gamemakers</p>
                <p>health: { this.state.health }</p>
                <p onClick={ this.healPlayer }><u>add</u></p>
            </div>
        )
    }
}

stores/playerstore.js

import { EventEmitter } from 'events'
import Dispatcher from '../dispatcher/dispatcher.js'
import { ActionTypes } from '../constants/actions'

const state = {
    health: 100,
    fatigue: 100
}

const stat = {
    strength: 1, // affects rof and damage
    agility: 1, // affects speed
    endurance: 1 // affects rate of fatigue
}

class PlayerStore extends EventEmitter {

    constructor() {
        super()

        // register this store with the dispatcher
        this.dispatchToken = Dispatcher.register(this.onAction.bind(this))
    }

    onAction(payload) {

        // provide hooks to each respective action
        switch(payload.type) {
            case ActionTypes.PLAYER_HEALED:
                this.increaseHealth()
                break
            case ActionTypes.PLAYER_INJURED:
                this.iHaveNoMadeThisBitYet()
                break
        }
    }

    get health() {
        return state.health
    }

    increaseHealth() {
        state.health += 6

        // broadcast an event declaring that the state has changed
        // so that whoever is listening to this store can update themself
        this.emit('change')
    }
}

module.exports = new PlayerStore();

dispatcher/dispatcher.js

const Dispatcher = require('flux').Dispatcher;

module.exports = new Dispatcher();

actions/playeractions.js

import { ActionTypes } from '../constants/actions'
import Dispatcher from '../dispatcher/dispatcher'

export default {

    healPlayer: function() {
        Dispatcher.dispatch({
            type: ActionTypes.PLAYER_HEALED
        })
    }

}

Here are my points:

  1. It feels weird to have my variables defined above my class and use public getters and so on to retrieve the values (instead of defining it on the constructor. Is there a benefit to this? It looks cleaner but it feels odd.
  2. Have I used import correctly? I want to share the Dispatcher across multiple files so I use import in those files. Will both of those files will contain the reference to the same dispatcher 'object'?
  3. Why do you often see that people take their actions out of the component and into an actions file? Is it to share actions between components?
  4. Shouldn't I just bind the actions straight onto the elements? In the UI I bind healPlayer which triggers an action. Is it okay just to bind the action?
  5. And following this, couldn't I also just dispatch the event through the Dispatcher in healPlayer instead of dispatching it in the actions/playeractions.js file?
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$
  1. Stores are just storages of data. You can implement it in any way you want.

  2. Yes and no. Yes, you're using import correctly. No, you don't need Dispatcher on the component since you have action files.

  3. The dispatcher is, well... a dispatcher. It marshals calls and route them to stores. On the other end, stores should only manipulate state and nothing more. There's no room for logic and feedback.

    Action files serve as an API layer. If the action isn't supported, the function won't be present and calling an undefined function throws an error. You can also serve deprecation notices, route calls to other actions, do complex actions, backwards compatibility logic, etc. Also works well for IDEs that have autocomplete.

  4. Yes

  5. Yes, but refer to #3 for the benefits of having an action file.

In general, the code looks ok. The idea of flux and react is to have a clear view of the data flow and logic. I wouldn't be surprised with the amount of code.

\$\endgroup\$
6
  • \$\begingroup\$ Regarding 2, it's a mistake that the Dispatcher has been imported into the component. However, does it still stand that playerstore and playeractions refer to the same Dispatcher? Maybe this question is a bit too big for a comment 😏 \$\endgroup\$
    – bitten
    Commented Jul 9, 2016 at 19:24
  • \$\begingroup\$ 3, "Action files serve as an API layer. If the action isn't supported, the function won't be present and calling an undefined function throws an error." That's a really nice explanation. Thanks. \$\endgroup\$
    – bitten
    Commented Jul 9, 2016 at 19:26
  • \$\begingroup\$ @bitten as far as I know, in flux (or at least in the flavor I use) there should only be one dispatcher so this should be fine. \$\endgroup\$
    – Joseph
    Commented Jul 10, 2016 at 1:14
  • \$\begingroup\$ So when I import the same reference to the same Dispatcher file across many stores, babel knows that I want to refer to the same object, right? When I first read import, it seems that it is going to instantiate a new Dispatcher in each store I import it into. Thanks for clearing that up. \$\endgroup\$
    – bitten
    Commented Jul 10, 2016 at 10:39
  • 1
    \$\begingroup\$ @bitten iirc, imports don't do copies/re-runs. It loads the module once and exposes it to the dependents. So loading the dispatcher module across files loads the same exact dispatcher module, not a copy/re-run of the file. \$\endgroup\$
    – Joseph
    Commented Jul 10, 2016 at 15:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.