我能够在服务器端预加载所有需要的状态,并将这些初始状态传递给客户端应用程序的redux存储。
我已启用redux logger以查看我的应用程序中发生了什么,并且正在从存储中重新提取状态。这是因为我的组件在componentDidMount
期间调用了必要的操作。以下是减速器、动作和组件的示例:
// action:
import axios from '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;
}
}
我使用的redux-promise-中间件
,它创建promise操作(实现,拒绝,挂起)。
在安装组件时调用该操作。我的理解是,即使在服务器端呈现组件以通知JS它存在,也会调用componentDidMount。组件本身不会重新安装。然而,我也读到,在componentDidMount
中运行我的操作是一个更好的选择,我个人认为,如果我从componentdiddupdate
调用它,它甚至不会工作(我们将进入一个无限循环)。
我想禁止调用这些初始操作,因为状态已经来自服务器端。我怎样才能做到这一点?
我希望有更好的解决方法,但到目前为止,我想到的是在接收数据时,根据服务器还是客户端接收数据,在reducer状态下设置ssr
标志。
首先,ComponentDidMount
不会在服务器上启动,只在客户端启动。因此,您需要另一种机制,在组件中使用react-router
match函数以及静态数据获取函数。有关具有易于理解的服务器端数据获取html" target="_blank">机制的良好样板文件,请参阅universal react。但是,您仍然需要在ComponentDidMount
方法中获取数据,否则,如果组件仅在客户端上呈现,或者在初始服务器呈现之后装载,则会出现问题。这就是在服务器上预加载数据,但在客户机上触发ComponentDidMount
时再次请求数据时可能出现的“双重获取”问题。
下面是一个解决此问题的示例实现(请注意,我使用的是redux格式,其中操作和还原程序位于一个文件中):
Redux/模块/app.js:
import { basepath, fetcher, fetchNeeded, checkStatus } from '../../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 from 'fetch-ponyfill'
import Promise from 'bluebird'
const { fetch, Request, Response, Headers } = fetchPonyfill(Promise)
const isClient = typeof document !== 'undefined'
export const basepath = isClient ? '': `${__APIROOT__}`
// check HTTP response status and 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 to triage fetch requests and SSR flags
export function fetchNeeded(statebranch, clearFlagSSR) {
if (statebranch.isFetching) return false
if (statebranch.ssr && isClient) {
clearFlagSSR()
return false
}
return true
}
// Fetch wrapper to set defaults and 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 into options header for 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)
}
其工作原理的基础是,如果服务器上发生了RECEIVE_DATA
操作,则redux状态下的ssr
标志设置为true
。否则设置为false
。
如果在客户机上调用了getData
函数,它首先调用fetchNeeded
函数(将状态树的这个特定部分与一个函数一起传递给它,该函数可以分派一个操作来清除ssr
标志)。如果ssr
标志为true
(当然我们是在客户机上),调用函数清除ssr
标志后,fetchNeeded
函数将返回false
。
因此,最后,如果服务器调用了我们的getData
函数,并且数据已经存在,那么客户端将不会第一次从服务器重新请求数据(而是清除ssr
标志)。清除标志后(或者如果从未设置标志),客户端将像正常情况一样从服务器请求数据。这允许我们避免对数据的初始冗余调用,但如果组件仅安装在客户机上或在客户机上刷新,则仍然有效。
希望这是有帮助的。
我是一名It专业的一年级学生。我们的任务是为最终项目的支付系统创建一个概念方案。我们决定做一个工资项目。下面是我使用JOptionFrame和JOptionPane的代码片段 由于分配给工资变量的数字取决于工作职位,为了计算准确,我必须从if语句中调用它。有人有解决方案吗?
假设我有一个名为的扩展,my_extension一个名为的插件。 然后,我的控制器中有一个名为的函数。 如何通过通常的http GET请求从外部调用此?
我是一名It专业的一年级学生。我们的任务是为最终项目的支付系统创建一个概念方案。我们决定做一个工资项目。下面是我使用JOptionFrame和JOptionPane的代码片段 由于分配给工资变量的数字取决于工作岗位,我必须从if语句中调用它,以便计算准确。有人有办法吗?
问题内容: 每次我都会收到警告: 抑制它的最佳方法是什么?所有软件包都是最新的。 Conf: OSX带有Brew Python 2.7.10(默认,2015年7月13日,12:05:58),pandas == 0.17.0和matplotlib == 1.5.0 问题答案: 您可以禁止所有警告:
我需要从JavaScript调用操作过程。我的操作接受2个输入参数和1个输出参数。下面是我的行动截图 有时还说 请求头字段access-control-allog-headers不允许access-control-allog-headers
我目前在JUnit测试中遇到了障碍,需要一些帮助。所以我用静态方法得到了这个类,它将重构一些对象。为了简化起见,我举了一个小例子。这是我的工厂课程: 这是我的测试类: 基本上,我试图实现的是私有方法check String()应该被抑制(因此不会抛出异常),并且还需要验证方法check String()实际上是在方法factorObject()中调用的。 更新:抑制正常工作,代码如下: ... 但