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

IT技术 javascript api reactjs components react-router
2021-05-11 18:00:11

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

这个想法是该组件是来自网站的文章列表,每个 Route 使用不同的 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只会在初始渲染之后被调用 ocne,因为您为每个路由使用相同的组件,因此componentDidMount不会再次被调用。您还需要使用componentwillreceiveprops生命周期方法,并检查您是否收到新的 APIkey,然后在其中执行 api 调用。

组件DidMount:

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

componentWillReceiveProps

componentWillReceiveProps() 在挂载的组件接收新的 props 之前被调用。如果您需要更新状态以响应 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>
    );
  }
}