Reputation: 1443
I'm working on building a simple hacker news app using React and Redux and I'm having trouble with rendering an individual article page component (the ArticleSingle components).
The routing works fine, but the ArticleSingle component does not render on click.
Here's my Root file, which handles all of the routing:
// REACT
import React from 'react';
import PropTypes from 'prop-types';
// REACT ROUTER
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
// REDUX
import { Provider } from 'react-redux';
import createBrowserHistory from 'history/createBrowserHistory';
const history = createBrowserHistory();
// COMPONENTS
import AppContainer from './components/containers/AppContainer';
import Home from './components/presentational/Home';
import ArticleSingle from './components/presentational/ArticleSingle';
const Root = ({ store }) => (
<Provider store={store}>
<Router history={history}>
<Switch>
<Route path="/" component={AppContainer} />
<Route path="/" component={Home} />
<Route path="/topics/:topic_name/articles" component={Home} />
<Route path="/articles/:article_id" component={ArticleSingle} />
</Switch>
</Router>
</Provider>
);
Root.propTypes = {
store: PropTypes.object.isRequired
};
export default Root;
Here's the ArticleSingle component, which is the one that I'm having trouble with. I ultimately want to get it to pass the id from the URL to an action passed down from AppContainer, but I was hoping to get the component to render something on the page (the id) before starting the redux work.
import React from 'react';
import PropTypes from 'prop-types';
const ArticleSingle = ({ params, children }) => (
<div>
{params.article_id}
{children}
</div>
);
export default ArticleSingle;
Here's the Home component, it features a filter bar, what why there are two routes for it
import React from 'react';
import PropTypes from 'prop-types';
// Components
import Spinner from 'react-spinkit';
import ArticleList from './ArticleList';
import TopicsSubNav from './TopicsSubNav';
const Home = ({
loading,
articles,
topics,
fetchTopicArticles,
fetchArticles,
children
}) => {
return (
<div>
<h3 className="title is-3">Northcoders News</h3>
<div id="TopicsSubNav">
{loading && <Spinner name="pacman" color="coral" fadeIn="none" />}
<TopicsSubNav
topics={topics}
onTopicClick={fetchTopicArticles}
onAllClick={fetchArticles}
/>
</div>
{loading && <Spinner name="pacman" color="coral" fadeIn="none" />}
<ArticleList articles={articles} topics={topics} />
{ children }
</div>
);
};
Home.propTypes = {
articles: PropTypes.array.isRequired,
topics: PropTypes.array.isRequired,
loading: PropTypes.bool,
fetchTopicArticles: PropTypes.func.isRequired,
fetchArticles: PropTypes.func.isRequired
};
export default Home;
Here's the AppContainer, controls the state for all of the app.
import React from 'react';
import PropTypes from 'prop-types';
import Home from '../presentational/Home';
import ArticleSingle from '../presentational/ArticleSingle';
// Redux
import { connect } from 'react-redux';
import * as actions from '../../actions/actions';
class AppContainer extends React.Component {
componentDidMount () {
this.props.fetchArticles();
this.props.fetchTopics();
}
render () {
return (
<div>
<Home
articles={this.props.articles}
topics={this.props.topics}
loading={this.props.loading}
fetchTopicArticles={this.props.fetchTopicArticles}
fetchArticles={this.props.fetchArticles}
/>
</div>
);
}
}
function mapDispatchToProps (dispatch) {
return {
fetchArticles: () => {
dispatch(actions.fetchArticles());
},
fetchTopics: () => {
dispatch(actions.fetchTopics());
},
fetchTopicArticles: topic => {
dispatch(actions.fetchTopicArticles(topic));
},
fetchArticleById: id => {
dispatch(actions.fetchArticleById(id));
}
};
}
function mapStateToProps (state) {
return {
articles: state.articles.data,
article: state.article,
topics: state.topics.data,
loading: state.loading
};
}
AppContainer.propTypes = {
articles: PropTypes.array.isRequired,
topics: PropTypes.array.isRequired,
loading: PropTypes.bool,
fetchArticles: PropTypes.func.isRequired,
fetchTopics: PropTypes.func.isRequired,
fetchTopicArticles: PropTypes.func.isRequired,
fetchArticleById: PropTypes.func.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(AppContainer);
Upvotes: 2
Views: 1579
Reputation: 3962
I see a couple of issues in your code.
1) Lets say you have <Route path="/articles/:article_id" component={ArticleSingle} />
- To access the article_id
in ArticleSingle Componenet you need to use props.match.params.article_id
2) This is your route:
<Route path="/" component={AppContainer} />
<Route path="/" component={Home} />
<Route path="/topics/:topic_name/articles" component={Home} />
<Route path="/articles/:article_id" component={ArticleSingle} />
Basically when you visit /articles/article-id you are matching the first route <Route path="/" component={AppContainer} />
and you are getting into it.
To solve this issue you can do one of the following:
A. use exact
in your first two routes. That would be <Route exact path="/" component={AppContainer} />
and <Route exact path="/" component={Home} />
B. Change the order of your routes and but the homepage root at the bottom like the following:
<Route path="/topics/:topic_name/articles" component={Home} />
<Route path="/articles/:article_id" component={ArticleSingle} />
<Route path="/" component={AppContainer} />
<Route path="/" component={Home} />
For the above solutions, I am assuming you are using React Router 4. I got the information I provided you from this tutorial: https://medium.com/@pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf
Upvotes: 2