React Axios - JSON Get response isn't displaying from render() - Riot API - javascript

I starting learning React a couple days ago and Axios today. I've spent the last 4+ hours watching/reading tutorials and I just can't figure this out.
I'm trying to create a simple stats website for League of Legends using Riot's API. Below you can see my constructor, componentDidMount, and render functions. I feel like I'm doing 1 of 3 wrong or most likely all 3. I'm calling this Get, which returns the JSON below. I want to access the "name" and "accountId".
{
"profileIconId": 3270,
"name": "Doublelift",
"puuid": "SrvIz_3Xa05InF_hTjwq1v8iB6lqNXz0SEc_5vhOFYlScrZOg8pSM9Si_UdPGAD9UYGhaRWHBeBGrw",
"summonerLevel": 155,
"accountId": "iNc_SUPKq-ckcANeC36Yn18Y0XSofK3ShBQg_h5wivC0Bg",
"id": "DjnxZhsTjgNhv3sMZMMJjlCUqAskiMfP6bP7GIcWovbwR1k",
"revisionDate": 1580499454000
}
I should note that I made my API key default. It's stored in my index.js file. Is this secure?
axios.defaults.headers.common['Authentication'] = 'API-Key-randomlettersandnumbers';
Here's my code. In render() when I type summonerDetail.[field] it recognizes the fields that are there shown in the JSON response above. Maybe my render is wrong causing it not to display? And yes I know "accountID" isn't in my render. I figured I'd start small with just "name". I will eventually need to use "accountID" for a different Get.
import React from 'react';
import axios from 'axios';
export default class GetBySummonerName extends React.Component {
constructor(props){
super(props);
this.state = {
summonerDetails: []
};
}
// https request to perform Get
componentDidMount() {
axios.get('https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/doublelift')
.then(res => {
console.log(res);
this.setState({ summonerDetails: res.data});
})
}
render() {
return (
<div>
{ this.state.summonerDetails.map(summonerDetail => <h1>{summonerDetail.name}</h1>)}
</div>
)
}
}
To display the "name" on the website I import the above class into App.js. Only problem is it's not working. I have the console.log(res); in my ComponentDidMount(), but I don't know how to view the console in Atom. I don't need any headers in my componentDidMount() because the "summonerName" is in the Get URL. The rest of the headers are auto-generated on Riot's side. Please help :)

You don't need to map. If you are receiving the object like the following in res.data
{
"profileIconId": 3270,
"name": "Doublelift",
"puuid": "SrvIz_3Xa05InF_hTjwq1v8iB6lqNXz0SEc_5vhOFYlScrZOg8pSM9Si_UdPGAD9UYGhaRWHBeBGrw",
"summonerLevel": 155,
"accountId": "iNc_SUPKq-ckcANeC36Yn18Y0XSofK3ShBQg_h5wivC0Bg",
"id": "DjnxZhsTjgNhv3sMZMMJjlCUqAskiMfP6bP7GIcWovbwR1k",
"revisionDate": 1580499454000
}
Then replace this in you return statement.
<div>{ this.state.summonerDetails.name }</div>
Hope this works for you.

You can check the response of your API in your network tab or you can console.log your state variable this.state.summonerDetails inside render method. If the response you are receiving is object, then you don't need to map over it. If it is an array then you have to iterate over it using map and extract the name property.

Related

How do i fetch json from a url and display json content on my js page

Im making a custom MS Teams app and in the app im trying to fetch a json from a url to then display the contents later. However, the fetch is failing. My goal is to fetch a list of data from a supplied url and then displaying it in the Tab within my teams app, which would be here: Where i want my json content to show up
As you can probably tell, i dont have any experience with javascript at all, but the custom MS teams app wants javascript...
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import React from 'react';
import './App.css';
import * as microsoftTeams from "#microsoft/teams-js";
/**
* The 'GroupTab' component renders the main tab content
* of your app.
*/
class Tab extends React.Component {
constructor(props){
super(props)
this.state = {
context: {}
}
}
//React lifecycle method that gets called once a component has finished mounting
//Learn more: https://reactjs.org/docs/react-component.html#componentdidmount
componentDidMount(){
// Get the user context from Teams and set it in the state
microsoftTeams.getContext((context, error) => {
this.setState({
context: context
});
});
// Next steps: Error handling using the error object
}
componentDidMount() {
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
}
render() {
var jsondata = data;
let userName = Object.keys(this.state.context).length > 0 ? this.state.context['upn'] : "";
return (
<div>
</div>
);
}
}
export default Tab;
So to sum it all up, how do i fetch my json from a url and display the content of that json on my tab.js page within teams?
Thanks in advance for any help.
While I can't speak to how the Teams API works, I can help you understand how to render things from a json API in your react component.
In your componentDidMount function, your example is sending and receiving the response from the API. To render this response, we need to assign the data to your component's "state" and then use that to render it in HTML.
This will be pretty simple. First, you need to extend your component's state, in a similar manner as you've done for context. Do this first in the constructor, where we'll declare an initial state of an empty object (I'll name it content but you can use whatever name makes most sense):
// inside the constructor() function
this.state = {
context: {},
content: {}
}
In React, we use setState to update this state object state when something changes, like on a lifecycle method such as componentDidMount. You just need to call this setState again when you want to change the state object from its initial value to something else. In our case, when we receive the data from the API.
setState takes whatever you provide it and merges it into the state object, so you only should declare anything you want to change. Anything else not declared will remain unchanged.
So, in componentDidMount, we can make a small change to do something with data when it arrives:
componentDidMount() {
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => {
this.setState({
content: data
})
});
}
This is basically saying:
once the component has mounted, make a call to fetch from the API
then, with that response, take the json from the body
and then, assign the json "data" into our component's state object under the key of content.
You can then do things with this data by calling this.state.content. I'm not sure what format the data will come in, but whatever json object arrives back from the API will be stored under this.state.content.
As an example, imagine we get a simple object back from the API that looks like this { title: "Tab title" }. It means that, on a successful call to the API, our state object will look like this:
{
context: "whatever you have here", // whatever you have here, I don't know this
content: { title: "Tab title" }
}
When this state object is updated, react will trigger a new render of the component.
So, to make it appear in our component, we need to use this state in our render function (we wrap things in curly braces if they need to be dynamically rendered rather than hardcoded):
render() {
return (
//... the rest of your function
<div>{this.state.content.title}</div>
);
}
As you might have guessed, this will show the title inside a div, if the title exists.
Eventually, you should consider handling the state of the component before that API call has resolved itself. The lifecycle method componentDidMount will be called after the component is mounted, and because you're hitting an API, there will be something rendered to the DOM before the API call resolves itself. In my example, it'll be just be an empty div, and then it'll appear when the state updates and the render function is called again.
You could do this more effectively by extending your state object to see whether the API response is done (you'd update this in the same place you set the content), and you could render the UI conditionally on this.
The official docs on lifecycle methods will help you understand this pattern more.
Good luck!
Well, you can rely on #Josh Vince for an explanation as he did it perfectly, I will just write the code a bit cleaner for you.
Let's create 2 methods that will set the state with respective data and then simply call the state values and render the following as per your wish.
import React, {Component} from 'react';
import './App.css';
import * as microsoftTeams from "#microsoft/teams-js";
class Tab extends Component {
this.state = {
context: {},
movies: null
}
getContext = () => {
try {
microsoftTeams.getContext((context) => this.setState({context}));
}
catch(error){
console.log('ERROR ->', error);
throw error;
}
}
fetchMoviesData = () => {
try {
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => this.setState({movies: data}));
} catch(error){
console.log('ERROR ->', error);
throw error;
}
}
componentDidMount() {
this.getContext();
this.fetchMoviesData();
}
render() {
let {movies, context} = this.state;
const jsondata = movies;
let userName = Object.keys(this.state.context).length ? context['upn'] : "";
return <>JSONDATA: {jsondata}, USERNAME: {userName}</>
}
}
export default Tab;

Sending a post request to jsonplaceholder fails in React Redux

I made a simple React Redux application that fetches a list of posts from jsonplaceholder and within it is a form that allows users to send a POST request. When I send a post request according to Redux DevTools extension it is added successfully marked as post number 101. Here is its snapshot
but the problem is after clicking the submit button 3 times it shows up on the screen.
The first two clicks show neither the title nor its body but it starts showing up on the third click.
This is Posts.jsx file and here is how I used componentDidUpdate to update the component after post request.
class Posts extends Component {
componentDidMount(){
this.props.fetchPosts();
}
componentDidUpdate(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
}
renderPosts(){ // cutted for brevity }
render() {
return (
{this.renderPosts()}
)
}
}
const mapStateToProps = (state) => {
return {
posts: state.posts.items,
newPost: state.posts.item,
}
}
export default connect(mapStateToProps, { fetchPosts })(Posts);
Here is its GitHub link repository.
The only error I am getting is the below error.
index.js:1 Warning: Each child in a list should have a unique "key" prop.
I don't believe this has anything to do with rendering the new post, but I already specified a "key" while looping through components.
What I am doing wrong during the course of this post request? Thank You.
You are using the wrong lifeCycle method. in order to get the nexProps you have to use componentWillReceiveProps instead of componentDidUpdate.
componentDidUpdate will give you the previous Props and previous State.
componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
}
The above snippet should work.
But this method is deprecated. react introduced an alternative (kind of) of this. which is called getDerivedStateFromProps. The problem is it is a static method and you can't access previous props (this.props) inside this method.
If you need it you did something wrong as it is an anti-pattern.

this.state.persons.map is not a function but persons is already an array

It's my first time working with React and I'm having some trouble with starting to use Axios. I watched a video, a very simple practical tutorial that showed how to use a get function, but I think something went wrong because even following the same exact steps I still get the error "this.state.persons.map is not a function". I want to stress the fact that the author of the video uses this exact same JavaScript code, and for him it works. Any explanation?
Here's the whole code for reference:
import React from "react";
import axios from "axios";
export default class personList extends React.Component{
state = {
persons: [],
};
componentDidMount(){
axios.get(`https://jsonplaceholder.typicode.com`)
.then(res =>{
console.log(res);
this.setState({persons: res.data});
});
}
render() {
return (
<ul>
{this.state.persons.map(person => <li key={person.id}>{person.name}</li>)}
</ul>
)
}
}
I looked around for an answer, but every other case that has been presented is either too different (using set arrays, json and whatnot) or it refers to a string used instead of an array, which causes the error, and obviously it's not my case.
You are making a GET request at https://jsonplaceholder.typicode.com which returns the whole webpage. If you want to fetch the users, use this URL instead: https://jsonplaceholder.typicode.com/users

Can't render out single page data from list in a react application using router and WordPress API

I’m pretty new to this so apologies in advance if I'm being dumb. I’m building a react application on top of the WordPress rest API. I’m trying to do something pretty basic which is to create a component showing a list of pages, each with a link which takes the user to a new view showing the individual ‘page’ with all the data for that page.
I’m almost there but am having problems outputting the correct data on the individual pages.
The approach I’ve taken is to take the id from match.params and then match it up with the page id passed down through props using javascript ‘find’.
This kind of works. I can console log the data for the individual page out from inside the ‘getPage’ method in the PageSingle component if I call it in the render method but the moment I try to access any individual values such as the id I get the old Cannot read property 'id' of undefined.
Probably not very clearly explained so please see code below:
PageList.js
import React from 'react';
import { Link } from 'react-router-dom';
const PageList = (props) => {
const pages = props.pages.map( (page) => {
return (
<div key={page.id}>
<Link to={`/pagelist/${page.id}`}>{page.title.rendered}</Link>
</div>
);
});
return <div className="page-list">{pages}</div>
};
export default PageList;
PageSingle.js
import React from 'react';
class PageSingle extends React.Component {
getPage = (props) => {
let thePage = props.pages.find(page => page.id === Number(props.match.params.pageId) );
**console.log(thePage); // 1. this works
console.log(thePage.id); // 2. this leads to error**
return thePage;
};
render() {
this.getPage(this.props);
return (
<h4>PageSingle</h4>
)
}
};
export default PageSingle;
JSON shown in console when it works – I’ve removed some so as not to take up too much space but you get the idea
{
content: {rendered: "Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. ", protected: false}
date: "2019-09-30T13:38:47"
excerpt: {rendered: "Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text.", protected: false}
id: 14
link: "http://localhost/all_projects/wordpress/sites_main/my_projects/portfolio/wordpress/test-page-2/"
slug: "test-page-2"
status: "publish"
title: {rendered: "Test Page 2"}
type: "page"
__proto__: Object
}
The props are sent to page single using Browser Router. The routes themselves are defined in the App.js component and look like this. Not sure if this is relevant or not, probably not.:
Routes
<Route
path="/pagelist" exact
render={ (props) => <PageList {...props} pages={ this.state.pages } /> }
/>
<Route exact
path="/pagelist/:pageId"
render={(props) => <PageSingle {...props} pages={ this.state.pages } /> }
/>
Obviously, the end goal is to eventually display the relevant data via the render method but I actually need to access that data before I can do that.
It’s probably something really basic that I’m just not understanding.
Any pointers in the right direction would be much appreciated.
Thanks.
If you have the correct data in your console.log then I think this is because console.log executes after a small delay,
Try doing this and see if you get the property
setTimeout(function()
{
console.log(thePage.id)
}, 100);
usually keys are added after the console.log call
Hope it helps
Okay, for anyone interested, the answer lay in the fact that an array with an object inside of it was being returned in PageSingle.js.
The answer therefore lay in grabbin gthe correct page with an if statement and pushing the required values to an array and then accessing that with bracket notation:
let thisPage = [];
props.pages.map((page) => {
if (page.id === Number(props.match.params.pageId)) {
thisPage.push(page.id, page.title.rendered, page.content.rendered, page.featured_images);
}
return thisPage;
})
return (
<div>
<h4>{thisPage[1]}</h4>
<p>{thisPage[2]}</p>
</div>
)
Got to be a less convoluted way of doing this though so, any suggestions please let me know.

This.setState does not set state even after binding `this`

I am currently building a new react project and I am having trouble setting state. I am making an ajax request to my backend to grab information, and then trying to set state, however, once I get out of the ajax scope, the state is not saved. I am binding this to ajax request but still no dice. The following is my code:
import React, { Component } from 'react';
import $ from 'jquery';
import SearchForm from './SearchForm.js';
class Main extends Component {
constructor() {
super()
this.state = {
actors: [],
genres: [],
directors: [],
}
this.getMovieInformation = this.getMovieInformation.bind(this);
}
getMovieInformation(movieName){
$.ajax({
url: 'http://localhost:3001/get_movie_data',
data: {name: movieName}
}).done(function(response){
this.setState({
actors: response.movieData.Actors.split(','),
genres: response.movieData.Genre.split(','),
writers: response.movieData.Writer.split(','),
directors: response.movieData.Director.split(',')
})
}.bind(this));
}
render() {
return (
<div>
<SearchForm getMovieInformation={this.getMovieInformation}/>
</div>
)
}
}
export default Main;
The funny thing is that I have done the same method of setting state inside ajax responses before (I also set bind(this) to the request) and my state remained outside of the ajax scope.
I've looked at other stackoverflow articles, everyone keeps saying to bind(this) after the ajax request, which I've been doing, There aren't any syntax errors, at least that I am aware of. Am I doing something wrong here?? This method has worked every time.
This line this.sendMovieTitleToAPI = this.getMovieInformation.bind(this); shouldn't it be this.getMovieInformation = this.getMovieInformation.bind(this);?
I also don't think you should be binding this in the ajax call.
I recommend using axios for making HTTP requests to your server.

Categories