如何使用 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>
)