This is the first time I am using react. I am coming from jQuery to React this feels like a big jump. If anybody can help me refactor this to work the React way I will be forever in your debt! :)
I am trying to parse an RSS feed, where I want to grab the most recent post title and link to render into a component.
https://www.npmjs.com/package/rss-parser - Using this to get the parser.
When viewing my app in the browser the async function is spitting out the rss feed into the console, which is a good start I guess!
// src/App/index.tsx
import * as React from 'react';
import * as Parser from 'rss-parser';
// Types
import { string } from 'prop-types';
let parser = new Parser();
// blueprint for the properties
interface Props {
name: string;
}
// Component state
interface State {
//feed: any[];
}
(async () => {
let feed = await parser.parseURL('https://www.reddit.com/.rss');
console.log(feed.title);
feed.items.forEach((item: { title: string; link: string; }) => {
console.log(item.title + ':' + item.link)
});
})();
export default class App extends React.Component<Props, State> {
render() {
return (
<div>
<h1>RSS Feed</h1>
<div>
<h1>item.title</h1>
item.link
</div>
</div>
);
}
}
If I understand you right, you need something like this:
export default class App extends React.Component<Props, State> {
constructor(props: {}) {
super(props);
this.state = { feed: [] };
}
async componentDidMount() {
const feed = await parser.parseURL('https://www.reddit.com/.rss');
this.setState({ feed });
}
render() {
return (
<div>
<h1>RSS Feed</h1>
this.state.feed.map((item, i) => (
<div key={i}>
<h1>item.title</h1>
item.link
</div>
))
</div>
);
}
}
I faced the same problem and solved by this. if you don't check for "undefined" value. It will show you error because react renders page 2 times and you have an undefined array of feed.items in your hand when in first render.
My index.js file:
import React from 'react'
import {render} from 'react-dom';
let Parser = require('rss-parser');
let parser = new Parser();
const CORS_PROXY = "https://cors-anywhere.herokuapp.com/";
class App extends React.Component{
constructor(props) {
super(props);
this.state = {
feed: []
};
}
async componentDidMount() {
const feed = await parser.parseURL(CORS_PROXY + 'https://www.reddit.com/.rss');
this.setState(feed)
}
render() {
return (
<div>
<h1>Blog Posts</h1>
{this.state.items && this.state.items.map((items, i) => (
<div key={i}>
<h1>{items.title}</h1>
{items.link}
</div>
))}
</div>
);
}
}
render(
<App />,
document.getElementById("root")
)
Related
This is driving me crazy. I just started playing around with react trying to learn, but i can't even make a simple API call because of a extremely weird error....
This is my code ->
import React, {Component} from 'react';
class ApiPosts extends Component {
constructor() {
super();
this.state = {
blogPosts: [],
};
}
}
componentDidMount(){
fetch('http://localhost:53595/blog/posts')
.then(results => {
return results.json();
}).then(data => {
let blogPosts = data.results.map((post) => {
return(
<div key={post.results}>
<div>{post.body}</div>
</div>
)
});
this.setState({blogPosts: blogPosts});
console.log("state", this.state.blogPosts);
})
}
render(){
return (
<div className="container2">
<div className="container1">
{this.state.blogPosts}
</div>
</div>
)
}
Lines 12 and 30 ("componentDidMount(){ & render(){") are throwing me an error showing that i haven't closed them with ';'.
The error shows up in visual studio code & fails to build my app with the following error ->
/react-website/src/ApiPosts.js: Unexpected token, expected ; (12:19)
I literally tried to close EVERYTHING in that file just to see where the error is coming from, but no luck.
Any idea?
Move componentDidMount and render function within your component class. They are outside of the class in given code snippet.
import React, {Component} from 'react';
class ApiPosts extends Component {
constructor() {
super();
this.state = {
blogPosts: [],
};
}
componentDidMount() {}
render() {}
}
You have componentDidMount and render defined outside of the component itself.
It should look like this:
import React, {Component} from 'react';
class ApiPosts extends Component {
constructor() {
super();
this.state = {
blogPosts: [],
};
}
componentDidMount() {
fetch('http://localhost:53595/blog/posts')
.then(results => {
return results.json();
}).then(data => {
let blogPosts = data.results.map((post) => {
return(
<div key={post.results}>
<div>{post.body}</div>
</div>
)
});
this.setState({blogPosts: blogPosts});
console.log("state", this.state.blogPosts);
})
}
render() {
return (
<div className="container2">
<div className="container1">
{this.state.blogPosts}
</div>
</div>
)
}
}
I got the object function using in react component, the below is my code, I tried to create an object function inside articleActions object, not got the syntax error. The api import is working fine and I get the right data and store in this component state: this.state.articles, this.state.authors.
App.js
import React from "react";
import DataApi from "../DataApi";
import data from "../testData";
import ArticleList from "./ArticleList";
const api = new DataApi(data.data);
class App extends React.Component {
constructor() {
super();
this.state = {
articles: api.getArticles(),
authors: api.getAuthors()
};
}
articleActions = {
lookupAuthor: authorId => this.state.authors[authorId]
};
render() {
return (
<ArticleList
articles={this.state.articles}
articleActions={this.articleActions}
/>
);
}
}
export default App;
the second file: ArticleList.js
import React from "react";
import Article from "./Article";
const ArticleList = props => {
return (
<div>
{Object.values(props.articles).map(article => (
<Article
key={article.id}
article={article}
actions={props.articleActions}
/>
))}
</div>
);
};
export default ArticleList;
the third file: Article.js
import React from "react";
const Article = props => {
const { article, actions } = props;
const author = actions.lookupAuthor(article.authorId);
return (
<div>
<div>{article.title}</div>
<div>{article.date}</div>
<div>
<a href={author.website}>
{author.firstName} {author.lastName}
</a>
</div>
<div>{article.body}</div>
</div>
);
};
export default Article;
The error message is :
SyntaxError: C:/Users/coral/Documents/react-advanced/lib/components/App.js:
Unexpected token (16:17)
14 | };
15 | }
> 16 | articleActions = {
| ^
17 | lookupAuthor: authorId => this.state.authors[authorId]
18 | };
the lookupAuthor should be a function with parameter:authorId, and get the return value of the author object. this.state.authors is the array of author objects. Each object with the authorId as the key, and author object as the value. I am not sure what is the error here when declare the function inside the js object. Hope someone can help
That should be method:
articleActions = () => ({
lookupAuthor: authorId => this.state.authors[authorId]
});
I started to use React Router earlier today for the first time, and I would like to embed an article's view inside the article's index. This works, the only problem is that when I want to change articles, the url changes, I get informations from the new one too, but nothing changes visually.
Here is my code from both the parent component and the child component:
articles.js
import React from 'react';
import utils from '../lib/functionsLibrary';
import Article from './article';
import {Link, Route} from "react-router-dom";
export default class Articles extends React.Component {
constructor(props) {
super(props);
this.state = {articles: null};
}
componentDidMount() {
let self = this;
utils.loader(window.location.origin + '/all_articles.json', function (articles) {
self.setState({articles: articles})
});
}
render() {
const {articles} = this.state;
const { match, settings } = this.props;
return (
<div>
<ul>
{
articles !== null &&
articles.map(function (a, i) {
return (
<li key={i}>
<Link to={`/articles/${a.slug}`}>{a.title}</Link>
</li>
)
})
}
</ul>
<Route path={`/articles/:slug`} component={Article} settings={settings}/>
<Route
exact
path={match.url}
render={() => <h3>Please select a topic.</h3>}
/>
</div>
)
}
}
article.js
import React from 'react';
import utils from '../lib/functionsLibrary';
export default class Article extends React.Component {
constructor(props) {
super(props);
this.state = {article: null};
}
componentDidMount() {
let self = this;
// I retrieve the list of articles ever saved, and filter them
// with the slug I passed as parameter / url
utils.loader(window.location.origin + '/all_articles.json', function (articles) {
self.setState({
article: articles.find(a => a.slug === self.props.match.params.slug)
});
});
}
render() {
// returns the correct data
console.log(this.props.match.params.slug);
const { article } = this.state;
return (
article !== null &&
<div>
<h1>{article.title}</h1>
<p>Tags: {article.tags}</p>
<p>{article.content}</p>
<img src={article.image.url} alt=""/>
</div>
)
}
}
Did I forgot about something?
Thank you in advance
Article component is not remounting when url params are changed, so componentDidMount method is not called. You should use componentWillReceiveProps method to check if params are changed.
componentDidMount() {
this.loadArticle(this.props.match.params.slug);
}
componentWillReceiveProps(newProps) {
if (this.props.match.params.slug !== newProps.match.params.slug) {
this.loadArticle(newProps.match.params.slug);
}
}
Working demo
I am building a simple movie catalogue using themoviedb API however I am facing an issue that I am unable to solve.
The issue is that the result after fetching is always undefined.
I tried with the method componentWillMount to fetching data and the setting the state inside this method but it does not work.
I tried to fetch inside constructor, no result.
This is my code so far
import React, { Component } from 'react';
import Header from './components/Header';
import MovieList from './components/MovieList';
import Footer from './components/Footer';
const MOVIE_API = "http://api.themoviedb.org/3/discover/movie?api_key=72049b7019c79f226fad8eec6e1ee889&language=en-US&sort_by=release_date.desc&include_adult=true&include_video=false&page=2&primary_release_year=2018";
//class
class App extends Component {
constructor(props){
super(props);
this.state = {
movies: [],
movieName: ''
}
}
componentWillMount(){
this.fetchMovie();
}
//fetching movie
fetchMovie = () =>{
const req = new Request(MOVIE_API, {
method: 'GET',
cache: 'default'
});
fetch(req).then(response =>{
return response.json();
}).then(data =>{
console.log(data); //REF 1;
this.setState({
movies: data
});
}).catch(err => {
console.log("ERROR: " + err);
})
}
render() {
return (
<div className="root">
<Header />
<MovieList moviesRes={this.state.movies}/>
<Footer />
</div>
);
}
}
export default App;
As you can see I called the method componentWillMount to fetch the data but it does not work.
It is also noticeable that if I log the data (REF 1) I can see the result (json).
===========================
EDIT
This is the code for MovieList
/*import React, { Component } from 'react';
export default class MovieList extends Component{
constructor(props){
super(props);
this.state = {
movies: this.props.movieRes
}
}
render(){
//if result is undefined
if(this.state.movieRes === undefined){
return(
<h1>Loading...</h1>
);
}else{
return(
<ul>
{this.state.movieRes.map((movie, index)=>{
return (
<li key={index}>{movie.title}</li>
);
})}
</ul>
);
}
}
}*/
=================
update child code
import React, { Component } from 'react';
export default class MovieList extends Component{
render(){
const { movieRes = [] } = this.props; // we are assigning a default prop here of an empty array.
return(
<ul>
{
//return movie from array
movieRes.map((movie, index)=>{
return (
<li key={index}>
{movie.id}
</li>
);
})
}
</ul>
);
}
}
In this I way I suppress the error, but still it is not working.
From what I learnt, React should render as soon as it detect changes but for some reason it not the case.
IMAGE
As you can see from the image when I am passing the array from parent component to the child component the array length is 20 but in the child component the array length seems to be 0
===================
Solution
I changed the component from class to a const and pass to it the array and everything went smooth. Here is the final code:
import React from 'react';
const MovieList = ({movies}) =>{
if(!movies){
return <h1>Loading...</h1>
}
return (
<ul>
{
movies.map((movie, index) => {
return (
<li key={index}>
<p>{movie.title}</p>
</li>
)
})
}
</ul>
);
}
export default MovieList;
Originally I misunderstood your issue but after re-reading it I noticed that you defined movies as an array in your constructor.
Without an actual error message, I'm going to assume that MovieList is expecting an array for it's prop movieRes and you're probably then trying to do something like .map or a loop to render the movies.
However, the API you're using doesn't return an array. It returns an object with an array key'd under results. So, I changed it to access data.results when doing setState.
//fetching movie
fetchMovie = () =>{
const req = new Request(MOVIE_API, {
method: 'GET',
cache: 'default'
});
fetch(req).then(response =>{
return response.json();
}).then(data =>{
console.log(data);
this.setState({
movies: data.results // <-- change made here.
});
}).catch(err => {
console.log("ERROR: " + err);
})
}
Here's a working JSFiddle:
https://jsfiddle.net/patrickgordon/69z2wepo/99513/
EDIT:
In the child component, instead of assigning props to state, just use props and default props.
import React, { Component } from 'react';
export default class MovieList extends Component{
render(){
const { movieRes = [] } = this.props; // we are assigning a default prop here of an empty array.
return(
<ul>
{movieRes.map((movie, index)=>{
return (
<li key={index}>{movie.title}</li>
);
})}
</ul>
);
}
}
I want to pass data from axiosDidMount function to
<p className='title' id='boldTitle'>{data goes here}</p>
I can console.log data and it is working and in my example it is a string "New York City".
I got to the point when I write some input in Search.js Component and it is passed to Results.js Component by this.props.userQuery. So the response.data[1][1] is updating correctly and live in console.log as I write input but I have problem with passing this data that I'm getting from Wikipedia to final destination.
What is proper way to pass this data in this example?
import React from 'react';
import axios from 'axios';
export default class Results extends React.Component {
axiosDidMount(userQuery) {
//const fruits = [];
const wikiApiUrl = 'https://en.wikipedia.org/w/api.php?action=opensearch&format=json&origin=*&search=';
const wikiApiUrlWithQuery = wikiApiUrl + userQuery;
axios.get(wikiApiUrlWithQuery)
.then(response => {
console.log(response.data[1][1]); //New York City
console.log(typeof(response.data[1][1])); //string
//console.log(response.data[2])
//console.log(response.data[3])
//fruits.push(response.data[1]);
})
.catch(err => {
console.log('Error: =>' + err);
});
//return fruits;
}
render() {
//this.props.userQuery from Search.js
const test = this.axiosDidMount(this.props.userQuery);
return(
<div>
<a className='title' href="" target='_blank'>
<div className='result'>
<p className='boldTitle'>{data goes here}</p>
<p></p>
</div>
</a>
</div>
);
}
}
You should separate your concerns. Make a data receiving component, or a container component that handles data retrieval and conditionally renders the component requiring the data once it's available. Something along the lines of the following:
import React, { Component } from 'react';
import axios from 'axios';
const PresentationComponent = (props) => {
// return mark with data
}
const PlaceHolderComponent = (props) => {
// return placeholder markup
}
export default class DataReceivingWrapper extends Component {
constructor(props) {
super(props);
this.state = {
data: null
}
}
componentDidMount() {
axios.get(...)
.then(data) {
this.setState(Object.assign({}, this.state, { data: data }))
}...
}
render() {
if (this.props.data) {
return <PresentationComponent />;
} else {
return <PlaceHolderComponent />; // or null
}
}
}