如何使用propTypes处理react / redux组件中的数据提取?

时间:2018-01-31 14:18:31

标签: javascript reactjs redux react-router-dom

有一个名为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,
}

使用DocumentContainer中的redux connect()函数创建Document组件。

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组件将文档作为必需的propType。但是在组件安装之后,文档才会被加载(调用动作fetchDocument)。所以我收到了这个警告:

  

index.js:2177警告:道具类型失败:道具document已标记   根据{{​​1}}的要求,但其值为Document

是否有解决此问题的最佳做法?

我考虑的解决方案:
  - 创建另一个名为DocumentLoader的组件,并在App中路由到该组件。然后,DocumentLoader将加载文档,并仅在文档加载后呈现Document组件。缺点是需要创建另一个组件。
  - 使用ownProps.match.params将文档加载到App组件的mapStateToProps函数中。然而,当App组件中需要加载10个其他组件时,这会变得很痛苦,而且我放弃了react-router-dom的一些简单性。

我最终做了以下事情:
- 将fetchDocument从componentDidMount移动到组件Document中的componentWillMount - 在提取开始后,将undefined添加到设置为isLoading的状态 - 在DocumentContainer中将true添加到document ? document : {}, - 将mapStateToProps替换为if(!document)

我仍然不确定的是,它是否是获取组件本身数据的最佳解决方案,或者是否有更好的方法。我没有找到任何资源来解释如何以任何其他方式执行此操作(例如,使用中间件)。

3 个答案:

答案 0 :(得分:1)

您需要将获取文档逻辑移至componentWillMount生命周期。

componentWillMount () {
  if (this.props.documentId) {
    this.props.fetchDocument(this.props.documentId)
  }
}

通常,您也不会在组件中调用fetch。你想使用某种中间件。当您开始提取文档时,您在商店中将isLoadingDocument标记为true。然后,不要执行if(!document),而是执行if(isLadingDocument)

之类的操作

答案 1 :(得分:0)

您已经处理过渲染函数中未定义文档的情况:

if (!document) {
  return <div>No document yet</div>
}

因此,您可以从isRequired移除document: PropTypes.object.isRequired, document: PropTypes.object,

由于您不需要document但未定义defaultProp,因此可能会出现掉落错误。要解决此问题,您可以指定defaultProps:

Document.defaultProps = {
  document: undefined,
};

答案 2 :(得分:0)

你可以在这里再考虑一件事,检查容器组件本身是否有文档,如果它不可用,只需返回一个空对象。

const mapStateToProps = (state, ownProps) => {
  const {documentId} = ownProps.match.params
  const document = state.documents.items[documentId]
  return {
    documentId: documentId,
    document: 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>
)