boardgame.io
boardgame.io copied to clipboard
stop destructuring state on the React client
We currently destructure the state object on the React client: source.
This allows you to access G as props.G rather than having to do props.state.G. However, it does cause name clashes between properties in the game state and other props that are passed to the React client (for example plugins).
The proposal is to stop destructuring the state object.
One thing that could be confusing about this is that state means something in React, so props.state is confusing. We'd have to rename it to something like gameState, which could also be confused with just the user-controlled game state G.
Maybe use a sort of namespace instead of state? Something like bgio?
I personally don’t mind overly the fact that props.state is similar to the React naming convention. It’s most confusing in class components where you could have this.state and props.state, but with function components it’s less likely there will be a variable actually named state.
This is also a prop that will very likely be destructured early in the component tree, so users will likely not be passing a state prop between lots of components. In fact, I would suggest showing something like this in documentation:
function Board (props) {
const { G, ctx } = props.state;
return (
<div>
<Score value={G.score} />
<TurnCounter count={ctx.turn} />
</div>
)
}
If you do think an alternative name is essential, might store be viable?
It might also make sense to discuss how to structure namespaces for plugins more generally before deciding for sure. Currently the situation we’re trying to resolve is:
{
plugins: {}, // plugin data
plugins: {}, // plugin dispatchers
}
If we add support for client plugins as briefly mentioned in #632, we might end up with a third plugins namespace. This RFC would resolve the current conflict, but how would we resolve that?
Instead of using plugins.chat() for the action dispatchers, perhaps we should use an explicit dispatch namespace freeing up plugins for other API details:
client.dispatch.chat({ message: 'Hello world!' })
Another question around the interface for plugins on the client:
Currently state is available at plugins[name].data, which mirrors the data/api distinction of plugins, but if we’re not going to combine these on the client — e.g. { data, dispatch, ...etc } — the data namespace might be unnecessarily verbose. state.plugins[name] should probably point directly to the data.
Re: the state destructuring, you could get the best of both worlds, by pre-destructuring the state fields you want as top-level properties (like G, ctx, isActive, and isConnected?) and passing those explicitly, and allowing the others to pass as something like gameState:
if (board) {
const { G, ctx, isActive, isConnected, ...gameState } = state;
_board = React.createElement(board, {
// ...state, // <=== removed
...this.props,
G, // <=== added
ctx, // <=== added
isActive, // <=== added
isConnected, // <=== added
gameState, // <=== added, all "other" state
isMultiplayer: !!multiplayer,
moves: this.client.moves,
events: this.client.events,
gameID: this.client.gameID,
playerID: this.client.playerID,
reset: this.client.reset,
undo: this.client.undo,
redo: this.client.redo,
log: this.client.log,
gameMetadata: this.client.gameMetadata,
});
}
I'm still very new to using boardgame.io, so I defer to you as to which values are "worthy" of being escalated to top-level props, and which ones should stay under gameState.
I would heavily object to keeping it as props.state:
const { G, ctx } = props.state;
const { a, b } = state;
It seems like it would be confusing for beginners and easy to mess the two up during brief glances of a code base. Knowing whether something is coming from state or props is really important in React contexts.