我一直在尝试使用react实现next服务器端呈现,而我使用with-redux-observable-app example,示例工作正常,但我想改进项目一点点做
componentWillMount
,这不会#39;满足我的#2条件我已将项目放在github上,并在此branch上提交了此特定问题
这是我到目前为止所做的总结
实现#1
// ./redux/index.js
...
import rootEpics from './root/epics'
import rootReducers from './root/reducers'
export default function initStore(initialState) {
const epicMiddleware = createEpicMiddleware(rootEpics)
const logger = createLogger({ collapsed: true })
const middlewares = applyMiddleware(thunkMiddleware, epicMiddleware, logger)
return createStore(rootReducers, initialState, middlewares)
}
// ./redux/root/epics.js
import { fetchCharacterEpic, startFetchingCharactersEpic } from '../ducks/Character/epics'
const rootEpics = combineEpics(
fetchCharacterEpic,
startFetchingCharactersEpic,
)
export default rootEpics
// ./redux/root/reducers.js
import { combineReducers } from 'redux'
import Character from '../ducks/Character'
const rootReducers = combineReducers({
Character,
})
export default rootReducers
// ./redux/ducks/Character/index.js
import * as types from './types'
const INITIAL_STATE = {
data: {},
error: {},
id: 1,
}
const Character = (state = INITIAL_STATE, { type, payload }) => {
switch (type) {
case types.FETCH_CHARACTER_SUCCESS:
return {
...state,
data: payload.response,
id: state.id + 1,
}
case types.FETCH_CHARACTER_FAILURE:
return {
...state,
error: payload.error,
}
default:
return state
}
}
export default Character
// ./redux/ducks/Character/types.js
export const FETCH_CHARACTER = 'FETCH_CHARACTER'
export const FETCH_CHARACTER_SUCCESS = 'FETCH_CHARACTER_SUCCESS'
export const FETCH_CHARACTER_FAILURE = 'FETCH_CHARACTER_FAILURE'
export const START_FETCHING_CHARACTERS = 'START_FETCHING_CHARACTERS'
export const STOP_FETCHING_CHARACTERS = 'STOP_FETCHING_CHARACTERS'
// ./redux/ducks/Character/actions.js
import * as types from './types'
export const startFetchingCharacters = () => ({
type: types.START_FETCHING_CHARACTERS,
})
export const stopFetchingCharacters = () => ({
type: types.STOP_FETCHING_CHARACTERS,
})
export const fetchCharacter = id => ({
type: types.FETCH_CHARACTER,
payload: { id },
})
export const fetchCharacterSuccess = response => ({
type: types.FETCH_CHARACTER_SUCCESS,
payload: { response },
})
export const fetchCharacterFailure = error => ({
type: types.FETCH_CHARACTER_FAILURE,
payload: { error },
})
// ./redux/ducks/Character/epics.js
import 'rxjs'
import { of } from 'rxjs/observable/of'
import { takeUntil, mergeMap } from 'rxjs/operators'
import { ofType } from 'redux-observable'
import ajax from 'universal-rx-request'
import * as actions from './actions'
import * as types from './types'
export const startFetchingCharactersEpic = action$ => action$.pipe(
ofType(types.START_FETCHING_CHARACTERS),
mergeMap(() => action$.pipe(
mergeMap(() => of(actions.fetchCharacter())),
takeUntil(ofType(types.STOP_FETCHING_CHARACTERS)),
)),
)
export const fetchCharacterEpic = (action$, id) => action$.pipe(
ofType(types.FETCH_CHARACTER),
mergeMap(() => ajax({
url: 'http://localhost:8010/call',
method: 'post',
data: {
method: 'get',
path: `people/${id}`,
},
})
.map(response => actions.fetchCharacterSuccess(
response.body,
true,
))
.catch(error => of(actions.fetchCharacterFailure(
error.response.body,
false,
)))),
)
实现#2
// ./pages/index/container/index.js
import React from 'react'
import { connect } from 'react-redux'
import { of } from 'rxjs/observable/of'
import rootEpics from '../../../redux/root/epics'
import { fetchCharacter } from '../../../redux/ducks/Character/actions'
import Index from '../component'
const mapStateToProps = state => ({
id: state.Character.id,
})
const mapDispatchToProps = dispatch => ({
async setInitialCharacter(id) {
const epic = of(fetchCharacter({ id }))
const resultAction = await rootEpics(
epic,
id,
).toPromise()
dispatch(resultAction)
},
})
export default connect(mapStateToProps, mapDispatchToProps)((props) => {
props.setInitialCharacter(props.id)
return (<Index />)
})
// ./pages/index/component/index.js
import React from 'react'
import Link from 'next/link'
import Helmet from 'react-helmet'
import Info from '../container/info'
const Index = () => (
<div>
<Helmet
title="Ini index | Hello next.js!"
meta={[
{ property: 'og:title', content: 'ini index title' },
{ property: 'og:description', content: 'ini index description' },
]}
/>
<h1>Index Page</h1>
<Info />
<br />
<nav>
{/* eslint-disable jsx-a11y/anchor-is-valid */}
<Link href="/other"><a>Navigate to other</a></Link><br />
<Link href="/about"><a>Navigate to about</a></Link>
{/* eslint-enable jsx-a11y/anchor-is-valid */}
</nav>
</div>
)
export default Index
// ./pages/index/container/info.js
import { connect } from 'react-redux'
import Info from '../../../components/Info'
const mapStateToProps = state => ({
data: state.Character.data,
error: state.Character.error,
})
export default connect(mapStateToProps)(Info)
与上述相关,获取工作正常,但......
我不希望抓取继续运行,我希望它只运行一次onEnter
。
为了实现这一目标,我写了一部名为startFetchingCharactersEpic()
的史诗,以及一部名为startFetchingCharacters()
的动作,最后在mergeMap(() => of(actions.stopFetchingCharacters())),
{{{{}} {{} 1}}参数,记住以下场景
fetchCharacterEpic()
pipe
startFetchingCharactersEpic()
setInitialCharacter
types.STOP_FETCHING_CHARACTERS
但是通过这样做我得到fetchCharacterEpic()
,控制台没有提供足够的信息,而不是说错误来自// ./pages/index/container/index.js
const mapDispatchToProps = dispatch => ({
async setInitialCharacter(id) {
const epic = of(startFetchingCharacters())
const resultAction = await rootEpics(
epic,
id,
).toPromise()
dispatch(resultAction)
},
})
尝试使用Google搜索问题,但发现与我的问题无关
更新
我设法根据下面的work again @jayphelps'设置answer,这让我回到原来的一些问题,
TypeError: Cannot read property 'type' of undefined
setInitialCharacter
但我猜这两个值得另一篇文章,因为我意识到我在这篇文章上提出了太多问题
答案 0 :(得分:1)
在这里完全猜测,但错误可能来自于你在这里发送承诺的事实:
const resultAction = await rootEpics(
epic,
id,
).toPromise()
dispatch(resultAction)
您的问题没有提及,但这意味着您必须拥有截取该承诺的中间件,因为redux(和redux-observable)仅预期POJO { type: string }
。
承诺也有可能不会解决undefined
以外的任何内容,在这种情况下,史诗中的ofType
运算符会窒息,因为它只适用于那些POJO操作{{1} }。
对不起,我不能更具体地说,很难遵循意图。
e.g。这个{ type: string }
看起来很奇怪,因为rootEpics是根史诗,并期望参数为await rootEpics(epic, id)
,UI组件不应该直接调用史诗?