Quick Start
Let's start from a simple case, just to get familiar with RocketJump. Basically, RocketJump provides just a function able to generate actions, reducers, selectors and sagas for a given API.
Now we are going to apply this function on a real case. Suppose we are developing a simple Todo app, and we want to fetch some ToDos from an asynchronous REST API. This requires just a plain GET request, which is, unsurprisigly, asynchronous! We can code the API call with a simple asynchronous function returning a Promise:
function loadTodosFromApi(params) {
let myHeaders = new Headers();
let config = {
method: 'GET',
headers: new Headers(),
body: params
};
return fetch('https://myawesomehost/api/posts', config)
.then(response => response.json())
}
Ok, now we are ready to integrate our loadTodosFromApi
asynchronous operation with Redux data flow. In order to do this, we need to generate
- some actions to handle the following events
- make the request
- request completes with success
- request completes with error
- a reducer to manage these actions and update the state in a consisten way
- some selectors to extract data from the state
- a saga to wire all it up
Redux-RocketJump can provide all these elements out of the box! This is all we need
- a name for the asynchronous task (it must be unique throughout your application)
- a path that describes where to store data in the redux store
- the asynchronous task we created before
This parameters must be passed in to RocketJump as a plain JavaScript object, like
{
type: 'GET_TODOS', // The name of the task
state: 'todos', // The path to store data at
api: params => loadTodosFromApi(params) // The asynchrous task
}
What we get is an object containing action dispatchers, reducer, selectors and saga. The keys of this object are the following
- actions: contains the actions dispatchers
- load: dispatches the action that starts the asynchronous task
- unload: dispatches an action to stop the asychronous task before it completes and to clear the state
- reducer: contains the reducer that manages data related to this task, i.e. the output, the errors and the state of our request
- saga: contains the saga that manages the side effects of the call
- selectors: contains selectors to access data regarding this task
- isLoading: retrieves the loading state of the application (
true
if the task is pending,false
otherwise) - getData: retrieves the data returned from the call when it completes with success
- getError: retrieves the error returned from the call when it completes, if any
- isLoading: retrieves the loading state of the application (
Now, let's put all these things together
This is our state/todos.js file
// Import rocketjump (rj for friends)
import { rj } from 'redux-rocketjump'
// Main export
// Here we deal with actions, reducers, selectors and sagas that have been created for us
export const {
// Actions generated by rj
actions: {
// This is the function that triggers the side effect
load: loadTodos,
// This function stops the asynchronous task started with `load` and clears the state
unload: unloadTods,
},
// Selectors generated by rj
selectors: {
// Selector used to get the value returned by the asynchronous task, when ready
getData: getTodos,
// Selector used to detect if the asynchronous task is running (i.e. the API call is loading)
isLoading: isLoading,
// Selector used to get the error with which the last API call failed (if available)
getError: getTodsError,
},
// The generated reducer
reducer,
// The generated saga
saga,
} = rj({
type: 'GET_TODOS',
state 'todos',
api: params => loadTodosFromApi(params),
})()
Please note that the rj function returns another function! We have to invoke this second function to get the actual generated items. The reasons of this necessity will be clarified in the API chapter
And this is our root state file (state/index.js)
import { createStore, compose, applyMiddleware, combineReducers } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { makeAppsReducers, makeAppsSaga } from 'redux-rocketjump'
import * as todos from './todos'
// Merge all our application parts
const APPS = {
todos
}
// Create root reducer
const rootReducer = combineReducers({
...makeAppsReducers(APPS),
})
// Create main saga
const mainSaga = makeAppsSaga(APPS)
// Initialize redux store
const preloadedState = undefined
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
rootReducer,
preloadedState,
compose(
applyMiddleware(sagaMiddleware),
)
)
// Start main saga
sagaMiddleware.run(mainSaga)
// Export the created store
export default store
Congratulations, you have set up your first RocketJump with success!