This week I learned a lot about the WikiEducationDashboard’s stack. I had to write Javascript, use Redux, write Mocha tests, rspec tests, and rails code for implementing the feature. This is not a full on tutorial because I didn’t implement it fully but rather my understanding of how it works.

The codebase of the dashboard already had a RevisionFeedbackController that rendered an HTML page for rendering the page. I wanted to import the same feedback to the React component.

So the first step was to also add a JSON output for the particular method of the controller. In my case it was RevisionFeedbackController#index so I added a index.json.jbuilder file in views/revision_feedback. Alternatively, you can render JSON output by

respond_to do |format|
 format.html # index.html.haml
 format.json { render :json => { 'key' : 'value' }
end

Now we need to retrieve and display the JSON output in React that is used for the dashboard’s frontend. The dashboard doesn’t use any gem to connect React with Rails. It rather includes the router.jsx in the asset pipeline. It uses the react-router package. And the router adds the Router element to all the DOM elements with element id ‘react_root’

const el = document.getElementById('react_root');
if (el) {
 ReactDOM.render((
 
 
 {routes}
 
 
 ), el);

You can learn more about React Router here. This import the right component for the right route (or URL) just like routes.rb in rails. Now for getting the JSON response in the react component, we use Redux. In the above code the element uses redux and stores the state tree of the React app in a single store and it is through these states we are going to fetch data from the Rails API and store it. You can learn more about Redux here. 

These are some Redux principles. The state is manipulated by actions despatched from within the component so that it doesn’t give hard to reproduce bugs and the state of the app doesn’t get messy. Redux attempts to make state mutations predictable. There are two kinds of components Presentation Components and Container Components. The crust of it is Presentation Components reads data from props and invokes some callbacks from props while Container Components are subscribed to Redux Store and dispatch redux actions.

In WikiEducationDashboard none of the components import the dispatch function or the redux store as <Provider> is the root element of the react app. This is how it was done in my code. The component as both Presentation and Container. I defined an action to fetch data from the Rails API.

Note: The code uses thunk to delay the action dispatch.

export function fetchFeedback(articleId) {
 return function (dispatch) {
 return API.fetchFeedback(articleId)
 .then((resp) => {
 dispatch({ type: types.FEEDBACK, data: resp, articleId: articleId });
 }
 )
 .catch(response => (ApiFailAction.fail(response)));
 };
}

Now to dispatch this function from a React Component

Note: bindActionCreators needs to be used as the component does not import the dispatch from redux or it is not redux aware.

import * as FeedbackAction from '../../actions/feedback_action.js';
.
.
.
const mapDispatchToProps = dispatch => ({
 fetchFeedback: bindActionCreators(FeedbackAction, dispatch).fetchFeedback
});

Now there is a prop fetchFeedback which you can call to dispatch the action which will change the state and cause the app to re-render. You might want to call it in componentWillMount() so that the action completes before rendering the component.

There is one final piece. Reducers. It uses the data received from the action to change the state of the app. It receives the current state and action response to create a new state.

export default function feedback(state = initialState, action) {
 switch (action.type) {
 case FEEDBACK: {
 const newState = { ...state };
 newState[action.articleId] = action.data.suggestions;
 return newState;
 }
 default:
 return state;
 }

Then you can map the state to a prop for referencing

const mapStateToProps = state => ({
 feedback: state.feedback
});

Then finally Redux provides an easy method connect to create a Container Component which is going to be same as the Presentation Component in this case.

export default connect(mapStateToProps, mapDispatchToProps)(Feedback);

Again the post is more about the big picture that I wanted to get so badly about the dashboard’s stack which uses Rails+Redux. So it might not serve as a good tutorial for beginners.

 

Advertisements