React and React Router, rendering the same element twice with a different prop results in two elements with the same prop?

元气小坏坏 提交于 2020-01-04 04:07:06

问题


I'm trying to use React with React-router v4 to render a few elements.

The idea is that the component is a list of articles from a website, with each Route using a different API key to get articles from a different site.

I'm using a single component to display the articles, and with each Route I want to pass a different API key.

The issue I have is that each route is using the same API key, which is the key of the first Route visited.

The source code is below:

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>

My two questions are, is this a good idea using a single component like this, even if it is rendering different information, or should I have a component that renders each different list of articles?

How can I fix it so the appropriate API key is used?


回答1:


I think reason is, componentDidMount will get called only ocne, just after the initial rendering, since you are using the same component for each route, so componentDidMount will not get called again. You need to use componentwillreceiveprops lifecycle method also, and check if you receive the new APIkey then do the api call inside that.

componentDidMount:

componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.

componentWillReceiveProps:

componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.

Write the component like this:

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>
    );
  }
}


来源:https://stackoverflow.com/questions/44008510/react-and-react-router-rendering-the-same-element-twice-with-a-different-prop-r

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!