reactjs - React Redux SSR how to suppress initial actions called from componentDidMount? -


i able preload needed states in server-side , pass these initial states client application's redux store.

i have enabled redux-logger see happens in application , states being refetched store. that's. because components call the necessary action during componentdidmount. here example reducer, action , component:

// action: import axios 'axios';  export function fetchnewstop() {     return {         type: 'fetch_news_top',         payload: axios.get('/news/top')     }; }  // reducer: export function newsarchive(state = {     pending: false,     response: { data: [] },     error: null }, action) {     switch (action.type) {     case 'fetch_news_top_pending':         return { ...state, pending: true, response: { data: [] }, error: null };     case 'fetch_news_top_fulfilled':         return { ...state, pending: false, response: action.payload.data, error: null };     case 'fetch_news_top_rejected':         return { ...state, pending: false, response: { data: [] }, error: action.payload };     default:         return state;     } }  // component: export class newsarchivefactory extends react.component {      componentdidmount() {         this.props.fetchnewstop();     }      render() {         if (this.props.news) {             return (                 <newsgrid items={this.props.news} />             );         }         return null;     } } 

i using redux-promise-middleware, creates promise actions (fulfilled, rejected, pending).

the action gets called when component mounted. understanding componentdidmount gets called if component rendered in server-side inform js exists. component doesn't remounted. however, have read better choice run actions in componentdidmount , think not work if call componentdidupdate (we enter infinite loop).

i want suppress these initial actions getting called because state coming server-side. how can achieve this?

i'm hoping there better way out there, far have come set ssr flag in reducer state upon receiving data based on whether data received on server or client.

first, componentdidmount not fire on server, on client. need mechanism uses react-router match function along static data fetching functions in components. see universal-react boilerplate has easy-to-understand server-side data fetching mechanism. however, still need fetch data in componentdidmount methods or things break if component rendered on client or mounted later after initial server render. introduces problem of potential "double fetches" when data pre-loaded on server gets requested again when componentdidmount fires on client.

below example implementation works around issue (note i'm using redux format actions , reducers in 1 file):

redux/modules/app.js:

import { basepath, fetcher, fetchneeded, checkstatus } '../../lib/api'  const isclient = typeof document !== 'undefined'  const set_flag_ssr =            'app/set_flag_ssr' const request_data =            'app/request_data' const receive_data =            'app/receive_data' const data_fail =               'app/data_fail' const invalidate_data =         'app/invalidate_data'  const initialstate = {   ssr: false,   initialized: false,   isfetching: false,   didinvalidate: true,   conditionfail: false,   data: {} }  export default function reducer(state = initialstate, action = {}) {   switch (action.type) {     case set_flag_ssr:       return object.assign({}, state, {         ssr: action.flag       })     case invalidate_data:       return object.assign({}, state, {         didinvalidate: true       })     case request_data:       return object.assign({}, state, {         isfetching: true,         conditionfail: false,       })     case receive_data:       return object.assign({}, state, {         ssr: !isclient,         initialized: true,         isfetching: false,         conditionfail: false,         didinvalidate: false,         lastupdated: action.receivedat,         data: action.data,       })     case data_fail:       return object.assign({}, state, {         isfetching: false,         conditionfail: true,       })     default:       return state   } }  function setflagssr(flag) {   return {     type: set_flag_ssr,     flag: flag,   } }  function requestdata() {   return {     type: request_data   } }  function receivedata(json) {   return {     type: receive_data,     data: json,     receivedat: date.now()   } }  function receivefail(err) {   return {     type: data_fail,     err: err,   } }  function fetchdata(url) {   return (dispatch, getstate) => {     dispatch(requestdata())     return fetcher(url, { credentials: 'include' }, getstate().cookie)     .then(response => response.json())     .then((json) => { return dispatch(receivedata(json)) })     .catch(err => {       dispatch(receivefail(err))     })   } }  export function getdata() {   return (dispatch, getstate) => {     const state = getstate()     if (fetchneeded(state.app, () => dispatch(setflagssr(false)))) {       return dispatch(fetchdata(basepath + '/api/app'))     }   } } 

lib/api.js:

import fetchponyfill 'fetch-ponyfill' import promise 'bluebird'  const { fetch, request, response, headers } = fetchponyfill(promise)  const isclient = typeof document !== 'undefined' export const basepath = isclient ? '': `${__apiroot__}`  // check http response status , throw error if not valid export function checkstatus(response) {   if (response.status >= 200 && response.status < 300) {     return response   } else {     let error = new error(response.statustext)     error.response = response     throw error   } }  // generic redux check triage fetch requests , ssr flags export function fetchneeded(statebranch, clearflagssr) {   if (statebranch.isfetching) return false   if (statebranch.ssr && isclient) {     clearflagssr()     return false   }   return true }  // fetch wrapper set defaults , inject cookie data on server (if present) export function fetcher(url, config={}, cookie={}) {   let options = {}   let headers = new headers();    // base headers   headers.set('accept', 'application/json');   headers.set('content-type', 'application/json');    if (config.headers) {     object.getownpropertynames(config.headers).foreach(function(key, idx, array) {       headers.append(key, config.headers[key])     })   }    if (cookie.value && !isclient) {     //inject cookie data options header server-to-server requests     headers.set('cookie', cookie.value)   }    options = {     credentials: config.credentials || 'same-origin',     method: config.method || 'get',     headers: headers,   }    if (config.body) options.body = config.body    return fetch(url, options) } 

the basics of how works ssr flag in redux state set true if receive_data action occurs on server. otherwise set false.

if getdata function called on client, first calls fetchneeded function (passing particular slice of state tree along function can dispatch action clear ssr flag). if ssr flag true (and of course on client) fetchneeded function return false after calling function clear ssr flag.

so in end, if server called our getdata function , data present client won't re-request data server first time (but instead clear ssr flag). after flag has been cleared (or if never set in first place) client request data server normal. allows avoid initial redundant call data still works if component mounted on client or gets refreshed on client.

hopefully helpful.


Comments

Popular posts from this blog

python - How to insert QWidgets in the middle of a Layout? -

python - serve multiple gunicorn django instances under nginx ubuntu -

module - Prestashop displayPaymentReturn hook url -