gpt4 book ai didi

reactjs - 如果需要在子路由上进行 react ,则路由器reux提取资源

转载 作者:行者123 更新时间:2023-12-03 13:39:34 25 4
gpt4 key购买 nike

我正在努力进行这项工作,这是我认为的一种常见模式,但我无法看到有关此示例或解决方案。

这是我正在处理的当前路线

/app/services/10/



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)
]
})


服务

现在问题出在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)
]
})


如您所见,childRoute Services有一个 fetchAll()异步请求,您可以想象,它需要 store中的某些数据,特别是商店中 user属性中的某些数据,例如userId或令牌。

如果我自然地导航,不会有问题。但是,当我刷新时,还没有填充 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)
})
}
})


其中 selectService只是一个过滤掉 state.services的函数

问题是 services是异步获取的,当您刷新该路由时,甚至在商店中的 store.dispatch完成并填充商店之前,是否执行了 services

如何解决这个异步问题?

最佳答案

TL; DR:使用组件的生命周期挂钩在需要时获取数据,如果道具未准备好,则有条件地呈现“正在加载”状态。或者使用HoC以更可重用的方式封装此行为。

您的问题很有趣,因为它不仅与react-router无关,而且与需要在渲染之前获取数据的任何react / redux应用程序无关。我们所有人在这个问题上至少经历了一次挣扎:“我在哪里获取数据?我如何知道是否加载了数据,等等。”这就是像Relay这样的框架要解决的问题。关于Relay的一件非常有趣的事情是,您可以为组件定义一些数据依赖关系,以使它们仅在其数据“有效”时才呈现。否则,将呈现“正在加载”状态。

通常,通过在componentDidMount生命周期方法中获取所需的数据并有条件地渲染微调器(如果道具尚未“有效”),通常会获得相似的结果。

在您的特定情况下,我理解正确,可以这样概括:


您使用react-router进入页面/services/
您的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>
)


效果很好。如果您已足够,可以在这里停止阅读此答案。如果您想要一种更好的可重用的方式来执行此操作,请继续阅读。

在此示例中,我们引入了某种形式的“我的数据是否可以渲染?否则如何使它们有效?”组件内部的逻辑。如果我们想在不同组件之间共享这种逻辑怎么办? As said by the doc


  在理想的情况下,您的大多数组件都是无状态功能,因为将来我们还可以通过避免不必要的检查和内存分配来针对这些组件进行性能优化。如果可能,这是推荐的模式。


我们在这里可以理解的是,我们所有的组件都应该是纯组件,而不要照顾其他组件或数据流(所谓的数据流,是指“是否提取了我的数据?”,等等)。因此,让我们仅使用纯组件来重写示例,而不必担心现在获取数据:

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
}


好的,到目前为止,我们有两个纯组件来定义它们需要哪些道具。而已。现在,我们需要将“在组件安装或呈现加载状态时,如果需要的话取数据”放置在某个地方。对于 Higher-Order Component或HoC来说,这是一个完美的角色。

简而言之,HoC使您可以将纯组件组合在一起,因为它们不过是纯函数。 HoC是一种函数,它以Component作为参数,然后返回用另一个包裹的Component。

我们想使服务的显示和获取它们的逻辑保持分离,因为正如我之前所说,您可能需要在另一个组件中获取服务的相同逻辑。 recompose是一个小的库,为我们实现了一些非常有用的HoC。我们在这里看


lifecycle添加 componentDidMount生命周期方法
branch规定是否获取服务的条件
renderComponent在获取服务时呈现一些 <LoadingComponent>
mapProps仅向 services组件提供 <Services>道具。
compose() utility让我们编写HoC而不是嵌套它们


因此,让我们构建负责以下内容的 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))
}


现在,无论组件何时需要商店中的 services,我们都可以像这样使用它:

const Loading = () => <p>Loading...</p>

const ServicesContainer = ensureServices(Services, Loading)


在这里,我们的 <Services>组件仅显示服务,但是例如,如果您有一个 <ServicesForm>组件需要 services来呈现每个服务的输入,我们可以编写类似以下内容:

const ServicesFormContainer = ensureServices(ServicesForm, Loading)


如果您不想推广这种模式,可以看看 react-redux-pledge,这是我拥有的一个小型库,用于处理此类数据依赖项。

关于reactjs - 如果需要在子路由上进行 react ,则路由器reux提取资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38206477/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com