Joe*_*ito 4 reactjs react-router redux
我正在努力做到这一点,这是我认为的一种常见模式,但我无法看到这方面的例子或解决方案.
这是我正在处理的当前路线
/app/services/10/
Run Code Online (Sandbox Code Playgroud)
app获取当前用户的登录信息/services获取服务的列表中的用户向他们提供/10获取服务10的细粒度细节因此,我使用一些数据填充商店的方式是:
import Services from './routes/Services'
export default (store) => ({
path: 'main',
getComponent (nextState, cb) {
require.ensure([], require => {
const App = require('./containers/AppContainer').default,
userActions = require('../store/user').actions
store.dispatch(userActions.fetch())
cb(null, App)
}, 'app')
},
childRoutes: [
Services(store)
]
})
Run Code Online (Sandbox Code Playgroud)
现在问题在于childRoutes:
import { injectReducer } from '../../../../store/reducers'
import Manage from './routes/Manage'
export default (store) => ({
path: 'services',
getComponent (nextState, cb) {
require.ensure([], require => {
const Services = require('./containers/ServicesContainer').default
const actions = require('./modules/services').actions
const reducer = require('./modules/services').default
store.dispatch(actions.fetchAll())
injectReducer(store, { key: 'services', reducer })
cb(null, Services)
})
},
childRoutes: [
Manage(store)
]
})
Run Code Online (Sandbox Code Playgroud)
正如你所看到的childRoute Services有一个fetchAll()异步请求,那你可以想像,需要从一些数据store,从具体事情user财产在店里,像例如用户id或令牌.
如果我自然导航,就不会有问题.但是当我刷新时,那个user道具尚未填充.
如果您无法看到这是一个问题,作为我的路线的一部分:
app/services/10
该参数10需要服务来自store,
export default (store) => ({
path: ':id',
getComponent ({params: {id}}, cb) {
require.ensure([], require => {
const Manage = require('./containers/ManageContainer').default
const ServicesActions = require('../../modules/integrations').actions
store.dispatch(ServicesActions.selectService(id))
cb(null, Manage)
})
}
})
Run Code Online (Sandbox Code Playgroud)
哪里selectService只是一个过滤掉的功能state.services
问题是services异步获取,当您刷新该路由时,store.dispatch甚至在services商店完成并填充商店之前执行获取?
如何处理此异步问题?
Pie*_*scy 13
TL; DR:使用组件的生命周期钩子在需要时获取数据,并在道具未准备好时有条件地呈现"加载"状态.或者使用HoC以更可重用的方式封装此行为.
您的问题很有意思,因为它不仅与react-router有关,而且与任何需要在呈现之前获取数据的react/redux应用程序相关.我们都在这个问题上至少挣扎过一次:"我在哪里获取数据?如何知道数据是否被加载等等".这就是Relay尝试解决的问题框架.关于Relay的一个非常有趣的事情是,您可以为组件定义一些数据依赖项,以便只有在它们的数据"有效"时才能呈现它们.否则,呈现"加载"状态.
我们通常通过在componentDidMount生命周期方法中获取所需数据来实现类似的结果,并且如果道具尚未"有效",则有条件地呈现微调器.
在你的具体情况下,我正确地理解它,它可以概括为:
/services/用react-router 命中了这个页面ServicesContainer加载所有服务/services/10,因为已经提取服务没有问题正如另一个答案所建议的那样,您可以通过在需要时获取数据来解决此问题,而不是在获取数据之前呈现服务.像这样的东西:
class Services extends React.Component {
componentDidMount() {
if (!this.props.areServicesFetched) {
this.props.fetchServices()
}
}
render() {
return this.props.areServicesFetched ? (
<ul>
{this.props.services.map(service => <Service key={service.id} {...service}/>)}
</ul>
) : <p>{'Loading...'}</p>
}
}
const ServicesContainer = connect(
(state) => ({
areServicesFetched: areServicesFetched(state) // it's a selector, not shown in this example
services: getServices(state) // it's also a selector returning the services array or an empty array
}),
(dispatch) => ({
fetchServices() {
dispatch(fetchServices()) // let's say fetchServices is the async action that fetch services
}
})
)(Services)
const Service = ({ id, name }) => (
<li>{name}</li>
)
Run Code Online (Sandbox Code Playgroud)
这很好用.如果这对你来说足够了,你可以在这里停止阅读这个答案.如果您想要一种更好的可重用方法,请继续阅读.
在这个例子中,我们介绍了一些"我的数据是否有效呈现,或者我怎样才能使它们有效?" 我们组件内的逻辑.如果我们想在不同的组件之间共享这个逻辑怎么办?正如文件所述:
在理想情况下,大多数组件都是无状态函数,因为将来我们还可以通过避免不必要的检查和内存分配来对这些组件进行性能优化.如果可能,这是推荐的模式.
我们在这里可以理解的是,我们所有的组件都应该是纯粹的,而不是处理其他组件,也不是数据流(我的意思是数据流,"我的数据是什么?"等).因此,让我们仅使用纯组件重写我们的示例,而不必担心现在的数据获取:
const Services = ({ services }) => (
<ul>
{services.map(service => <Service key={service.id} {...service}/>)}
</ul>
)
Services.propTypes = {
services: React.PropTypes.arrayOf(React.PropTypes.shape({
id: React.PropTypes.string,
}))
}
const Service = ({ id, name }) => (
<li>{name}</li>
)
Service.propTypes = {
id: React.PropTypes.string,
name: React.PropTypes.string
}
Run Code Online (Sandbox Code Playgroud)
好的,到目前为止,我们有两个纯组件定义了他们需要的道具.而已.现在,我们需要在某个地方放置"在组件安装或渲染加载状态时根据需要获取数据".它是高阶组件或HoC 的完美角色.
简而言之,HoC可以让您将纯组件组合在一起,因为它们只不过是纯函数.HoC是一个函数,它将Component作为参数并返回包含另一个Component的Component.
我们希望将服务的显示和获取它们的逻辑分开,因为正如我之前所说,您可能需要在另一个组件中获取服务的相同逻辑.recompose是一个小库,为我们实现了一些非常有用的HoC.我们在这里看
componentDidMount生命周期方法<LoadingComponent>在获取服务时呈现一些services我们的<Services>组件提供prop .所以让我们构建我们的ensureServices函数,负责:
connect redux商店的纯组件services如果需要,获取services尚未从服务器收到加载状态,则渲染services收到时我们的组件呈现这是一个实现:
const ensureServices = (PureComponent, LoadingComponent) => {
/* below code is taken from recompose doc https://github.com/acdlite/recompose/blob/master/docs/API.md#rendercomponent */
const identity = t => t
// `hasLoaded()` is a function that returns whether or not the the component
// has all the props it needs
const spinnerWhileLoading = hasLoaded =>
branch(
hasLoaded,
identity, // Component => Component
renderComponent(LoadingComponent) // <LoadingComponent> is a React component
)
/* end code taken from recompose doc */
return connect(
(state) => ({
areAllServicesFetched: areAllServicesFetched(state), // some selector...
services: getServices(state) //some selector
}),
(dispatch) => ({
fetchServices: dispatch(fetchServices())
})
)(compose(
lifecycle({
componentDidMount() {
if (!this.props.areAllServicesFetched) {
this.props.fetchServices()
}
}
}),
spinnerWhileLoading(props => props.areAllServicesFetched),
mapProps(props => ({ services: props.services }))
)(PureComponent))
}
Run Code Online (Sandbox Code Playgroud)
现在,无论组件需要services来自商店的任何地方,我们都可以像这样使用它:
const Loading = () => <p>Loading...</p>
const ServicesContainer = ensureServices(Services, Loading)
Run Code Online (Sandbox Code Playgroud)
在这里,我们的<Services>组件只显示服务,但是如果你有一个<ServicesForm>需要services为每个服务呈现输入的组件,我们可以写下这样的东西:
const ServicesFormContainer = ensureServices(ServicesForm, Loading)
Run Code Online (Sandbox Code Playgroud)
如果你不想概括这个模式,你可以看看react-redux-pledge,一个我拥有的小型库来处理这种数据依赖.
| 归档时间: |
|
| 查看次数: |
2072 次 |
| 最近记录: |