如何使用 propTypes 处理 react/redux 组件中的数据获取?
How to handle data fetch in react/redux component with propTypes?
有一个名为 Document 的组件可以显示文档的详细信息。
Document.js
export class Document extends Component {
componentDidMount () {
if (this.props.documentId) {
this.props.fetchDocument(this.props.documentId)
}
}
render () {
const {document} = this.props
if (!document) {
return <div>No document yet</div>
}
return (
<div>
<h1>Document {document.name}</h1>
</div>
)
}
}
Document.propTypes = {
documentId: PropTypes.string.isRequired,
document: PropTypes.object.isRequired,
fetchDocument: PropTypes.func.isRequired,
}
Document 组件是使用 DocumentContainer 中的 redux connect() 函数创建的。
DocumentContainer.js
const mapStateToProps = (state, ownProps) => {
const {documentId} = ownProps.match.params
const document = state.documents.items[documentId]
return {
documentId: documentId,
document: document,
}
}
const mapDispatchToProps = dispatch => {
return {
fetchDocument: (documentId) => {
dispatch(fetchDocument(documentId))
},
}
}
const DocumentContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Document)
export default DocumentContainer
名为 App 的主组件路由到 DocumentContainer 组件。
App.js
class App extends Component {
render () {
return (
<div>
<Switch>
<Route path="/document/:documentId" component={DocumentContainer}/>
</Switch>
</div>
)
}
}
export default withRouter(connect(mapStateToProps)(App))
问题是 Document 组件将 document 作为必需的 propType。但是只有在组件挂载后才会加载文档(调用操作 fetchDocument)。所以我收到这个警告:
index.js:2177 Warning: Failed prop type: The prop document
is marked
as required in Document
, but its value is undefined
是否有解决此问题的最佳实践?
我考虑的解决方案:
- 创建另一个名为 DocumentLoader 的组件并在 App 中路由到该组件。然后 DocumentLoader 加载文档,并且仅在文档加载后才呈现 Document 组件。缺点是需要创建另一个组件。
- 使用 ownProps.match.params 在 App 组件的 mapStateToProps 函数中加载文档。然而,当有 10 个其他组件需要加载到 App 组件中时,这会变得很痛苦,并且我失去了一些 react-router-dom.
的简单性
我最终做了以下事情:
- 将组件文档中的 fetchDocument 从 componentDidMount 移动到 componentWillMount
- 添加一个 isLoading
到设置为 true
的状态,一旦抓取开始
- 在 DocumentContainer
中将 document ? document : {},
添加到 mapStateToProps
- 将 if(!document)
替换为 if(isLoading)
我仍然不确定的是,在组件本身中获取数据是否是最佳解决方案,或者是否有更好的方法。我没有找到任何资源来解释如何以任何其他方式(例如使用中间件)执行此操作。
您需要将获取文档逻辑移至 componentWillMount
生命周期。
componentWillMount () {
if (this.props.documentId) {
this.props.fetchDocument(this.props.documentId)
}
}
通常您也不会在组件中调用 fetch
。你想使用某种中间件。当您开始获取文档时,您在商店中将标志 isLoadingDocument
设置为 true
。然后而不是做 if(!document)
你做一些像 if(isLadingDocument)
.
您已经处理了渲染函数中未定义文档的情况:
if (!document) {
return <div>No document yet</div>
}
所以您可以从 document: PropTypes.object.isRequired,
中删除 isRequired
,它变成 document: PropTypes.object,
。
这可能会引发 linting 错误,因为您不需要 document
但没有为其定义 defaultProp
。要解决这个问题,您可以指定 defaultProps:
Document.defaultProps = {
document: undefined,
};
你可以在这里多考虑一件事,在容器组件本身检查文档是否可用,如果不可用,就return一个空对象。
const mapStateToProps = (state, ownProps) => {
const {documentId} = ownProps.match.params
const document = state.documents.items[documentId]
return {
documentId: documentId,
document: document ? document : {},
}
}
您还应该像这样检查文档组件中的空对象,
const {document} = this.props
if (Object.keys(document).length === 0) {
return <div>No document yet</div>
}
return (
<div>
<h1>Document {document.name}</h1>
</div>
)
有一个名为 Document 的组件可以显示文档的详细信息。
Document.js
export class Document extends Component {
componentDidMount () {
if (this.props.documentId) {
this.props.fetchDocument(this.props.documentId)
}
}
render () {
const {document} = this.props
if (!document) {
return <div>No document yet</div>
}
return (
<div>
<h1>Document {document.name}</h1>
</div>
)
}
}
Document.propTypes = {
documentId: PropTypes.string.isRequired,
document: PropTypes.object.isRequired,
fetchDocument: PropTypes.func.isRequired,
}
Document 组件是使用 DocumentContainer 中的 redux connect() 函数创建的。
DocumentContainer.js
const mapStateToProps = (state, ownProps) => {
const {documentId} = ownProps.match.params
const document = state.documents.items[documentId]
return {
documentId: documentId,
document: document,
}
}
const mapDispatchToProps = dispatch => {
return {
fetchDocument: (documentId) => {
dispatch(fetchDocument(documentId))
},
}
}
const DocumentContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Document)
export default DocumentContainer
名为 App 的主组件路由到 DocumentContainer 组件。
App.js
class App extends Component {
render () {
return (
<div>
<Switch>
<Route path="/document/:documentId" component={DocumentContainer}/>
</Switch>
</div>
)
}
}
export default withRouter(connect(mapStateToProps)(App))
问题是 Document 组件将 document 作为必需的 propType。但是只有在组件挂载后才会加载文档(调用操作 fetchDocument)。所以我收到这个警告:
index.js:2177 Warning: Failed prop type: The prop
document
is marked as required inDocument
, but its value isundefined
是否有解决此问题的最佳实践?
我考虑的解决方案:
- 创建另一个名为 DocumentLoader 的组件并在 App 中路由到该组件。然后 DocumentLoader 加载文档,并且仅在文档加载后才呈现 Document 组件。缺点是需要创建另一个组件。
- 使用 ownProps.match.params 在 App 组件的 mapStateToProps 函数中加载文档。然而,当有 10 个其他组件需要加载到 App 组件中时,这会变得很痛苦,并且我失去了一些 react-router-dom.
我最终做了以下事情:
- 将组件文档中的 fetchDocument 从 componentDidMount 移动到 componentWillMount
- 添加一个 isLoading
到设置为 true
的状态,一旦抓取开始
- 在 DocumentContainer
中将 document ? document : {},
添加到 mapStateToProps
- 将 if(!document)
替换为 if(isLoading)
我仍然不确定的是,在组件本身中获取数据是否是最佳解决方案,或者是否有更好的方法。我没有找到任何资源来解释如何以任何其他方式(例如使用中间件)执行此操作。
您需要将获取文档逻辑移至 componentWillMount
生命周期。
componentWillMount () {
if (this.props.documentId) {
this.props.fetchDocument(this.props.documentId)
}
}
通常您也不会在组件中调用 fetch
。你想使用某种中间件。当您开始获取文档时,您在商店中将标志 isLoadingDocument
设置为 true
。然后而不是做 if(!document)
你做一些像 if(isLadingDocument)
.
您已经处理了渲染函数中未定义文档的情况:
if (!document) {
return <div>No document yet</div>
}
所以您可以从 document: PropTypes.object.isRequired,
中删除 isRequired
,它变成 document: PropTypes.object,
。
这可能会引发 linting 错误,因为您不需要 document
但没有为其定义 defaultProp
。要解决这个问题,您可以指定 defaultProps:
Document.defaultProps = {
document: undefined,
};
你可以在这里多考虑一件事,在容器组件本身检查文档是否可用,如果不可用,就return一个空对象。
const mapStateToProps = (state, ownProps) => {
const {documentId} = ownProps.match.params
const document = state.documents.items[documentId]
return {
documentId: documentId,
document: document ? document : {},
}
}
您还应该像这样检查文档组件中的空对象,
const {document} = this.props
if (Object.keys(document).length === 0) {
return <div>No document yet</div>
}
return (
<div>
<h1>Document {document.name}</h1>
</div>
)