Background:
The website contains a signup form which consists of a Languages dropdown option. In order to get language options from the database, we need to call an AJAX get request.
Problem:
1) Where to trigger the Ajax call in the react life cycle.
2) How to use redux to handle UI changes after AJAX call.
3) How to use reqwest to do it.
P.S: I encountered problems in using isomorphic fetch with Django, because the version I used won’t store the cookie sent back from the Django server (set-cookie header) so I used reqwest instead.
Code:
SignupPage.js:
We apply the thunkMiddleware, the reason will be discussed later
import 'babel-polyfill' import React from 'react'import ReactDom from 'react-dom' import {createStore, applyMiddleware} from 'redux' import thunkMiddleware from 'redux-thunk' import reducer from './reducers/signup'import SignupApp from './components/SignupApp' import { Provider } from 'react-redux' let store = createStore( reducer, applyMiddleware( thunkMiddleware )); ReactDom.render( <Provider store={store}> <SignupApp /> </Provider> , document.getElementById('signup-box') );
SignupApp.js:
nothing special
import React from 'react' import SignupFormContainer from '../containers/SignupFormContainer' const SignupApp = () => ( <div style={{height: '100%'}}> <div className='ui middle aligned center aligned login grid'> <div className='login column'> <SignupFormContainer /></div> </div> </div> ) export default SignupApp
SignupFormContainer.js:
redux-form is used here, if you are not using redux-form, you can replace reduxForm() to connect() from react-redux:
import { reduxForm } from 'redux-form'; import SignupForm from '../components/signupForm' import { register } from '../actions/signupFormAjaxAction' const mapDispatchToProps = (dispatch) => { return { submit: (e, values)=> { e.preventDefault(); dispatch(register(values)); } } }; // map reducer's state to react component's props const mapStateToProps = (state) => { return { // languageReducer defaultLanguage: state.fetchLanguage.defaultLanguage, languages: state.fetchLanguage.languages, // LoginFromReducer formInitialized: state.signup.formInitialized, showError: state.signup.showError, errorMsg: state.signup.errorMsg, fieldsStyle: state.signup.fieldsStyle } }; export default reduxForm({ form: 'SignupForm',// a unique name for this form fields: ['first_name', 'last_name', 'username', 'password', 'email', 'display_language'] // all the fields in your form }, mapStateToProps, mapDispatchToProps)(SignupForm)
SignupForm.jsx (React Component):
We use the AJAX call in componentDidMount():
class SignupForm extends Component { componentDidMount() { const {dispatch} = this.props; dispatch(fetchLanguage()) }...... }
signupFromAjaxAction.js:
The fetchLanguage() can return a function that allows we can delay and have branchings in dispatch.
Detail: https://www.npmjs.com/package/redux-thunk
Short summary:
– fetchLanuage: Start AJAX request
– requestLanuageOption: Waiting server’s response: maybe trigger UI doing loading animation
– receiveLanguageOption: Ppdate UI based on the response
export const receiveLanguageOption = (languages, osLanguage) => { return { type: 'RECEIVE_LANGUAGE_OPTION', languages: languages, osLanguage: osLanguage, }; }; export function fetchLanguage() { return function (dispatch) { dispatch(requestLanguageOption()); return reqwest({ url: '/accounts/languages/', type: 'json', contentType: 'application/json' }).then( // then will be excuted after receving response // success and dispatch the language options to the reducer json => dispatch(receiveLanguageOption(json.languages, json.os_language)), // fail (err, msg) => console.log(msg) ); }; };
languageReducer.js:
the reducer will set the state and the state will be passed to all react components. Using object.assign can avoid mutation.
const initialState = { languages: [], defaultLanguage: null }; const languageReducer = (state = initialState, action) => { switch (action.type) { case 'REQUEST_LANGUAGE_OPTION': return state; case 'RECEIVE_LANGUAGE_OPTION': return Object.assign({}, state, { languages: action.languages, defaultLanguage: action.osLanguage }); default: return state; } }; export default languageReducer;
SignupForm.jsx:
<select className='ui dropdown search' name='display_language' id='display_language' {... display_language}> {this.props.languages.map((language, i) => ( <option key={i} value={language[0]}>{language[1]}</option>)} </select>