Getting Started With Gambit
Gambit is a library for use with Redux and React that makes building API driven web-apps very easy.
The idea behind Gambit is that Redux is a great tool (thanks Dan Abramov) but it is necessarily low-level. This can make a lot of the common tasks of building API driven apps a bit of an ordeal.
Gambit provides an interface for making those common tasks much easier and more pleasant. A quick example is creating a contained component that has to speak to an API. Taking a simple stateless component that displays a list of tweets:
const TweetList = ({ tweets, currentUser }) => {
return (
<div>
{tweets.map(tweet => ({
<p>{tweet.get('content')}</p>
<div>
<span>Posted By:</span>
<span>
{tweet.get('user') === currentUser.get('name') ? 'You' : tweet.get('user')}
</span>
})}
</div>
);
};
With Gambit, connecting this to your store and api is simple:
const TweetListContainer = createContainer(TweetList, {
fetch: {
tweets: {
as: state => state.twitter.get('tweets'),
grab: dispatch => currentValue => {
if (currentValue.count() === 0) return dispatch(getMyTweets());
},
},
currentUser: {
as: state => state.user.get('currentUser'),
grab: dispatch => currentValue => {
if (!currentValue) return dispatch(getCurrentUser());
},
},
},
pending() {
return <h2>Loading</h2>;
},
});
Whereas in plain Redux, your container would need to be something more complicated and harder to follow (adapted from Redux: Read me):
class TweetListContainer extends Component {
doFetching(props = false) {
const { tweets, currentUser } = props || this.props;
if (!tweets.count()) dispatch(getMyTweets());
if (!currentUser) dispatch(getCurrentUser());
}
componentDidMount() {
this.doFetching();
}
componentWillReceiveProps(nextProps) {
this.doFetching(nextProps);
}
render() {
const {
tweets,
isFetchingUser,
isFetchingTweets,
currentUser,
} = this.props;
return (
{isFetchingUser || isFetchingTweets (
<h2>Loading...</h2>
)}
{!isFetchingUser && !isFetchingTweets (
<TweetList tweets={tweets} currentUser={currentUser} />
)}
);
}
}
TweetListContainer.propTypes = {
tweets: PropTypes.array.isRequired,
currentUser: PropTypes.object.isRequired,
isFetchingTweets: PropTypes.bool.isRequired,
isFetchingUser: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state) {
return {
tweets: state.twitter.get('tweets'),
isFetchingTweets: state.twitter.get('isFetchingTweets'),
isFetchingUser: state.twitter.get('isFetchingUser'),
currentUser: state.user.get('currentUser'),
};
}
export default connect(mapStateToProps)(TweetListContainer);
Gambit also decreases the boilerplate required for other parts of this down the chain:
- Creating ActionCreators that fire actions when a call has started, is finished and has failed.
- Ensuring ActionCreators only cause API calls when desired, not just when a component is loaded.
- Creating Reducers to update state