问题
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