Problem
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:
- 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.
- Have I used
import
correctly? I want to share theDispatcher
across multiple files so I useimport
in those files. Will both of those files will contain the reference to the same dispatcher ‘object’? - 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?
- 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? - And following this, couldn’t I also just dispatch the event through the
Dispatcher
inhealPlayer
instead of dispatching it in theactions/playeractions.js
file?
Solution
-
Stores are just storages of data. You can implement it in any way you want.
-
Yes and no. Yes, you’re using
import
correctly. No, you don’t needDispatcher
on the component since you have action files. -
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.
-
Yes
-
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.