React 和 React Router,使用不同的 prop 两次渲染相同的元素会导致两个元素具有相同的 prop?

Posted

技术标签:

【中文标题】React 和 React Router,使用不同的 prop 两次渲染相同的元素会导致两个元素具有相同的 prop?【英文标题】:React and React Router, rendering the same element twice with a different prop results in two elements with the same prop? 【发布时间】:2017-10-15 22:50:09 【问题描述】:

我正在尝试将 React 与 React-router v4 一起使用来渲染一些元素。

这个想法是该组件是来自网站的文章列表,每个路由使用不同的 API 密钥从不同的网站获取文章。

我使用单个组件来显示文章,并且我想通过每个 Route 传递不同的 API 密钥。

我遇到的问题是每条路由都使用相同的 API 密钥,这是访问的第一个 Route 的密钥。

源码如下:

var APIKeys = 
    BBCKey: 'https://newsapi.org/v1/articles?source=bbc-sport&sortBy=top&apiKey=foo',
    ESPNKey: 'https://newsapi.org/v1/articles?source=espn&sortBy=top&apiKey=foo',
    FourFourTwoKey: 'https://newsapi.org/v1/articles?source=four-four-two&sortBy=top&apiKey=foo'


let App = () => (
    <BrowserRouter>
      <div className="container">
      <header>
        <span className="icn-logo"><i className="material-icons">code</i></span>
        <ul className="main-nav">
          <li><NavLink exact to="/">Home</NavLink></li>
          <li><NavLink to="/BBCSport">BBC Sport</NavLink></li>
          <li><NavLink to="/FourFourTwo">FourFourTwo</NavLink></li>
          <li><NavLink to="/ESPN">ESPN</NavLink></li>
        </ul>
      </header>
      <Switch>
        <Route exact path="/" component=Home />
        <Route path="/BBCSport" render= () => <GetArticles APIKey=APIKeys.BBCKey />  />
        <Route path="/FourFourTwo" render= () => <GetArticles APIKey=APIKeys.FourFourTwoKey />  />
        <Route path="/ESPN" render= () => <GetArticles APIKey=APIKeys.ESPNKey />  />
        <Route component=NotFound />
      </Switch>
      </div>
    </BrowserRouter>
  );

export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

import React,  Component  from 'react';
import axios from 'axios';
import ArticleList from './ArticleList';

export default class GetArticles extends Component 

  constructor(props) 
    super(props);
    this.state = 
      articleTitles: [],
      loading: true
    ;
  

  componentDidMount() 
    axios.get(this.props.APIKey)
      .then(response => 
        let titles = response.data.articles.map( (currentObj) => 
          return currentObj.title;
         );

        this.setState(
          articleTitles: titles,
          loading: false
        );
      )
      .catch(error => 
        console.log('Error fetching and parsing data', error);
      );
  

  render() 
    return (
      <div>
        <div className="main-content">
          <h1 className="main-title">Articles</h1>
           (this.state.loading) ? <p>Loading</p> :   <ArticleList list=this.state.articleTitles /> 
        </div>
      </div>
    );
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

我的两个问题是,使用这样的单个组件是否是个好主意,即使它呈现不同的信息,还是应该有一个组件来呈现每个不同的文章列表?

如何修复它以便使用适当的 API 密钥?

【问题讨论】:

【参考方案1】:

我认为原因是,componentDidMount 只会在初始渲染之后被调用,因为您为每条路由使用相同的组件,所以componentDidMount 不会再次被调用。您还需要使用componentwillreceiveprops生命周期方法,并检查您是否收到新的APIkey,然后在其中进行api调用。

componentDidMount:

componentDidMount() 在组件被调用后立即调用 安装。需要 DOM 节点的初始化应该放在这里。如果你 需要从远程端点加载数据,这是一个很好的地方 实例化网络请求。在此方法中设置状态将 触发重新渲染。

componentWillReceiveProps

componentWillReceiveProps() 在挂载组件之前调用 收到新道具。如果您需要更新状态以响应 prop 更改(例如,重置它),您可以比较 this.props 和 nextProps 并使用 this.setState() 执行状态转换 这个方法。

像这样编写组件:

export default class GetArticles extends Component 

  constructor(props) 
    super(props);
    this.state = 
      articleTitles: [],
      loading: true
    ;
  

  componentDidMount() 
    console.log('initial rendering', this.props.APIKey)
    this._callApi(this.props.APIKey);
  

  componentWillReceiveProps(newProps)
     if(newProps.APIKey != this.props.APIKey)
        this._callApi(newProps.APIKey);
        console.log('second time rendering', newProps.APIKey)
     
  

  _callApi(APIKey)
    axios.get(APIKey)
      .then(response => 
        let titles = response.data.articles.map( (currentObj) => 
          return currentObj.title;
         );

        this.setState(
          articleTitles: titles,
          loading: false
        );
      )
      .catch(error => 
        console.log('Error fetching and parsing data', error);
      );
  

  render() 
    return (
      <div>
        <div className="main-content">
          <h1 className="main-title">Articles</h1>
           (this.state.loading) ? <p>Loading</p> :   <ArticleList list=this.state.articleTitles /> 
        </div>
      </div>
    );
  

【讨论】:

我认为这与生命周期方法有关,但这并不能解决问题,我会继续玩。 做一件事,把console.log(this.props.APIKey)放到render方法里面,改变路由的时候显示结果。 console.log() 也放入componentDidMountcomponentWillRecieveProps 方法中,并检查它们是否正在更改路线。 我已将 console.log() 放置在每个方法中。在 render() 方法中,它返回正确的 API 密钥,但是没有输出其他 console.logs()。 哦抱歉,componentWillReceiveProps 函数中有拼写错误尝试更新的代码,并检查控制台日志值,它应该可以工作:)

以上是关于React 和 React Router,使用不同的 prop 两次渲染相同的元素会导致两个元素具有相同的 prop?的主要内容,如果未能解决你的问题,请参考以下文章

React-router v4教程

React+router和react+redux使用过程的记录

React路由基础

使用具有不同片段字段的相同中继根查询的多个 react-router-relay 路由

react-router详细解释

react-router(不同组件之间传递路由)