I'm making a react-express app, and am pulling the following data from a SQLite3 database:
[
{
id:1,
name: 'henry',
photo: '/Photos/dog1.jpg'
},
{
id:1,
name: 'boris',
photo: '/Photos/dog2.jpg'
},
{
id:1,
name: 'Borker',
photo: '/Photos/dog3.jpg'
}
]
The back end is working fine, and returns the above object upon making GET requests in postman. I'm using the following set up:
//Route
router.get('/', (req,res) => {
db.getDogs()
.then(dogs => {
res.json(dogs)
})
})
//API
export const getDogs = () => {
axios.get('/v1/dogs')
.then(res => {
console.log(res.data) // **returns [object Object]**
return res.data
})
}
//API call in react component
import React from 'react'
import {getDogs} from '../api/indexApi'
class App extends React.Component {
constructor(){
super()
this.state = {
dogs:[]
}
}
componentDidMount() {
getDogs()
.then(dogs => {
this.setState({
dogs:dogs
})
})
}
The problem is that the data I'm pulling is not rendering in the browser, and I think it has something to do with my axios get request - consoling logging the reponse from that request gave [object Object]. Any suggestions on how to fix this? Thanks.
It seems to me that you didn't provide the full API URL to axios, if you haven't created baseURL for axios, just provide the full URL localhost:800/api/dogs:
export const getDogs = () => {
return axios.get('localhost:8000/v1/dogs')
.then(res => {
console.log(res.data)
return res.data
})
}
You should be good to go!
To create baseURL for axios, DRY
Related
I am developing an application in react and for the first time I decided to implement tests. I started with the simpler components, which I still had trouble with, but now I would like to test more complex components, especially with API calls via axios.
My app looks like this:
import * as React from "react";
import Axios from "axios";
import { AnyData } from "../../interface/AnyDataInterface";
interface IProps {
}
interface IState {
loading: boolean | undefined,
myData: AnyData | undefined
}
export default class Home extends React.Component <IProps, IState> {
constructor (props: IProps){
super(props);
this.state = {
loading: true,
myData: {
field: undefined,
...
}
};
}
fetchData = ():void => {
this.setState({ loading: true });
Axios.get<AnyData>('my-url')
.then(res => {
if (res.status === 200 && res != null) {
this.setState({ myData:{... res.data} });
this.setState({ loading: false });
} else {
console.log('problem when fetching the logs from backend');
}
})
.catch(error => {
console.log(error);
});
}
render():JSX.Element {
return(
<div className="container">
<div>
<button role="button" onClick={this.fetchData}>Search</button>
</div>
{this.state.loading == true ? <p>Wait</p>:
<div role="composite">
{ Some child component that will render data }
</div>
}
</div>
);
}
}
My test looks like this:
import axios from 'axios';
import React from 'react';
import { render, screen, fireEvent, waitFor } from '#testing-library/react';
import Home from './Home';
jest.mock('axios');
describe('Home component', () => {
test('it can be clicked', async () => {
const fakeData = [{
some_field: some_data,
...
}];
axios.get = jest.fn().mockResolvedValue(() =>
Promise.resolve({data: fakeData}));
render(<Home />);
fireEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(axios.get).toBeCalledWith("my-url");
});
await waitFor(() => {
//Both expect should pass, if the title is there the data should also appear
expect(screen.getByText('some title text')).toBeInTheDocument(); //Pass without error
expect(screen.getByText('the data from fakeData that should be there')).toBeInTheDocument(); //Send me an error because jest is unable to find some_data
});
});
});
When I wrote this test it failed every time but the problem was not in my test, the problem was in my data fetch function. In the .then I had a condition which was the following: if (res.status === 200 && res != null) { and the res.status === 200 was a condition never satisfied.
So I removed this condition and the call goes through normally. But I had to change a part of my code that is normally not problematic, is there a way to mock the status of the response too ? So that I can put this condition back in my function.
I have another problem that appears now, in my test my component displays well the information "after click" but each field that appears has its corresponding data empty, the data seems not to pass although the .then passes and does not send me to the .catch.
What seems strange to me is that by removing the condition res.status === 200 in my data fetch function and leaving the condition res != null, the test still passes (with the same empty data problem). Finally, by adding a console.log("data: ", res); in my .then in my fetch data function of my component I get, when the test is executed, the following log: data: [Function (anonymous)].
I don't know if this could explain this behavior but my test is in javascript while my classes and functions are in typescript. But the data I fill in my test has the right form (the same as the type requested in my class) so typescript should see that the type matches and have no particular problem
If anyone knows how to answer any of these questions it would really help me, I've been stalling on this test for a while and it's starting to drive me crazy.
EDIT:
I just realized that in fetchData() in my component I am trying to access res.data but the fake data I am sending in my test is in the form fakeData = [{...}]. Could this be the reason why my object is empty?
I tried to change this fake data in my test to: fakeData = [{ data: { ...}}] but I still have the same problem of empty response when calling the get method of axios.
EDIT:
I've try to change test file to a .tsx file and change my "fakeData" in my test to:
jest.mock('axios');
describe('Home component', () => {
test('it can be clicked', async () => {
const fakeData: AnyData = {
"some_field": some_data,
...
};
const fakeConfig: AxiosRequestConfig<any> = {
"maxBodyLength": -1
};
const fakeHeaders: AxiosResponseHeaders= {
"access-control-allow-credentials": "true",
"access-control-allow-headers": "*",
"access-control-allow-methods": "*",
"access-control-allow-origin": "some-url",
"access-control-expose-headers": "scrollId",=
"content-type": "application/json; charset=utf-8",
....
"x-powered-by": "Express"
}
const res: AxiosResponse<AnyData, any> = {
config: fakeConfig,
data: fakeData,
headers: fakeHeaders,
status: 200,
statusText: "OK",
};
axios.get = jest.fn().mockResolvedValue(() =>
Promise.resolve({res: res}));
render(<Home />);
fireEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(axios.get).toBeCalledWith("my-url");
});
...
});
});
But even with this method it still doesn't work, yet I really have the impression that the problem comes from the formatting of the data I am sending and I don't see how my data differs from the one requested in fetchData(). As for the fields in my data, I'm sure they're good, so I guess the problem is with the reading of res.data.
When I make the new test by putting a console.log of myData in fetchData() I get the following data: { some_field: undefined, ... }
Thanks in advance if you take the time to help me.
I finally realized my mistake on my test. It was in the rewriting of the axios.get method.
The code that works is as follows:
jest.mock('axios');
describe('Home component', () => {
test('it can be clicked', async () => {
const fakeData: AnyData = {
"some_field": some_data,
...
};
const fakeConfig: AxiosRequestConfig<any> = {
"maxBodyLength": -1
};
const fakeHeaders: AxiosResponseHeaders= {
"access-control-allow-credentials": "true",
"access-control-allow-headers": "*",
"access-control-allow-methods": "*",
"access-control-allow-origin": "some-url",
"access-control-expose-headers": "scrollId",=
"content-type": "application/json; charset=utf-8",
....
"x-powered-by": "Express"
}
const res: AxiosResponse<AnyData, any> = {
config: fakeConfig,
data: fakeData,
headers: fakeHeaders,
status: 200,
statusText: "OK",
};
(axios.get as jest.Mock).mockResolvedValue(res); //THE LINE THAT CHANGE EVERYTHING
render(<Home />);
fireEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(axios.get).toBeCalledWith("my-url");
});
...
});
});
I am new to the react and I am learning how to HTTP get request using axios. I am referring to a youtube video: https://www.youtube.com/watch?v=NEYrSUM4Umw and following along, but I got an error. I think I have minor error but I can't figure it out.
import React, { Component } from 'react'
import axios from 'axios'
class PostList extends Component {
constructor(props) {
super(props)
this.state = {
posts: []
}
}
componentDidMount(){
axios.get('https://api.particle.io/v1/devices/64646468431646/temperature?access_token=547376a1b2')
.then(response => {
console.log(response)
this.setState({posts: response.data})
})
.catch(error => {
console.log(error)
}
)
}
render() {
const { posts } = this.state
return (
<div>
temperature
{
posts.length ?
posts.map(post => <div key={post.coreInfo.deviceID}> {post.result} </div>) :
null
}
</div>
)
}
}
export default PostList
When I use postman to get a HTTP request, I get the following response:
{
"cmd": "VarReturn",
"name": "temperature",
"result": "67.55",
"coreInfo": {
"last_app": "",
"last_heard": "2020-04-05",
"connected": true,
"last_handshake_at": "2020-04-05",
"deviceID": "64646468431646",
"product_id": 8
} }
My goal is to display the result: 67.55 in the web application.
Thank you in advance
If you're only getting a single object as the response from your fetch instead of an array, just wrap it in an array -
this.setState({posts: [response.data]})
I have a chat UI in react in which the user should input text and receive some data from an API response service. The UI works fine and the user text is presented in the chat but the response from the API which received in JSON format doesn't presented in the chat at all (not even an error message from the API, which should appear in the 'message' field,which is the same field where the result should be presented).As far As I know I configured everything ok, I don't know if something is messing in the render method though.
Note: the preconfigured messages at the chat state in the beginning are just for testing purposes.
The Fetch operation in under the componentDidUpdate() method at the first block of code that I posted.
import React from "react";
import ReactDOM from "react-dom";
import "./App.css";
import Message from "./Message.js";
class Chatroom extends React.Component {
constructor(props) {
super(props);
this.state = {
chats: [
{
username: "clientUser",
content: <p>Hello World!</p>,
img:
"http://***.jpg"
},
{
username: "user2",
content: <p>Hi,my name is user2.What's up ??</p>
},
{
username: "user3",
content: <p>Hi,my name is user3.What's up ??</p>
},
{
username: "user4",
content: <p>Hi,my name is user4.What's up ??</p>
},
{
username: "userN",
content: <p>Hi,my name is userN.What's up ??</p>,
img: "http://***.jpg"
},
{
username: "user5",
content: <p>Hi,my name is user5.What's up ??</p>
},
{
username: "user6",
content: <p>Hi,my name is user6.What's up ??</p>
},
{
username: "user7",
content: <p>Hi,my name is user7.What's up ??</p>
}
]
};
this.submitMessage = this.submitMessage.bind(this);
}
componentDidMount() {
this.scrollToBot();
}
componentDidUpdate() {
this.scrollToBot();
fetch(
"https://****",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
}
).then(response => response.json())
.then(parsedJSON =>
parsedJSON.results.map((
user
) => ({
username: "BotResponse",
content: `${user.message}',
img: "http://***.jpg"
}))
)
.then(chats =>
this.setState({
chats
})
)
.catch(error => console.log("parsing failed", error));
}
scrollToBot() {
ReactDOM.findDOMNode(this.refs.chats).scrollTop = ReactDOM.findDOMNode(
this.refs.chats
).scrollHeight;
}
The submit message method:
submitMessage(e) {
e.preventDefault();
this.setState(
{
chats: this.state.chats.concat([
{
username: "clientUser",
content: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>,
img: "http://***.jpg"
}
])
},
() => {
ReactDOM.findDOMNode(this.refs.msg).value = "";
}
);
}
The render method:
render() {
const username = "clientUser";
const { chats } = this.state;
return (
<div className="chatroom">
<h3>
Title
</h3>
<ul className="chats" ref="chats">
{chats.map((
chat //Defines message component
) => (
<Message chat={chat} user={username} />
))}
</ul>
<form className="input" onSubmit={e => this.submitMessage(e)}>
<input type="text" ref="msg" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default Chatroom;
The problem lies in the way you think. You are thinking like if you were writing plain HTML with JS enhancements. React is a fundamental concept shift. You need to forget a lot of what you knew from regular, vanilla JS, to learn React. In React, you don't get HTMLElement's to do operations but rather work with local/global state and component props to declare your UI. The resulting code is more performant, maintainable and glitch-free.
To get back to your actual issue, you shouldn't be using ref to get the value of your input. Instead, use onChange to set state of your message and retrieve it later when needed.
For example:
export class MessageComposer extends React.Component {
state = {
fields: {
message: ""
}
}
clearField = fieldName => {
this.setState({
fields: {
[fieldName]: ""
}
})
}
onInputChange = e => {
this.setState({
fields: {
[e.target.name]: e.target.value
}
})
}
sendMessage = () => {
const { fields: { message } } = this.state
// Here you send the message contained into `message`.
console.log(message)
// Then you clean the message input value.
this.clearField("message")
}
render () {
const { fields: { message } } = this.state
return (
<div className="MessageComposer__container">
<input
type="text"
name="message"
value={message}
onChange={this.onInputChange}
/>
<button onClick={this.sendMessage}>
Submit
</button>
</div>
)
}
}
EDIT: Just saw that your fetch code includes JSX. WTF?
Try by changing the following:
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
with JSX-free version. It doesn't make any sense to do pass a React component as JSON.stringify isn't even able to serialize a function (ok you'll receive i.e 'Function' or '[Object object]' into inputText on your backend i guess).
Somthing like this:
body: JSON.stringify({
inputText: ReactDOM.findDOMNode(this.refs.msg).value
})
Also, if for any reason React doesn't find your ref (i.e. component returns null or any other reason), the inputText will either throw or return undefined.
If you refactor your code as I suggested above, you could do this:
body: JSON.stringify({
inputText: this.state.fields.message
})
There is a fair amount to discuss regarding your code. To keep this answer simple and focused, I am just going to address your fetch api call and why you may not be seeing anything on the front end.
First, I suggest looking into Life Cycle Methods in the React Documentation. You can find that here. If that link is broken, just go to React's documentation page and search "LifeCycle Methods".
The reason I bring that up is because you are posting your data every time the component updates not when the user submits their chat message. So, the moment the user does submit you call your onSubmit function which calls setState. Setting the state will update the post. So anywhere in your code, when you make changes to your state or if there are changes to your component's props the DOM will update and componentDidUpdate will be triggered and you will post again.
Place your post api call in your onSubmit function. Update your form from this:
submitMessage(e) {
e.preventDefault();
this.setState(
{
chats: this.state.chats.concat([
{
username: "clientUser",
content: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>,
img: "http://***.jpg"
}
])
},
() => {
ReactDOM.findDOMNode(this.refs.msg).value = "";
}
);
}
to this updated onSubmit function:
submitMessage(e) {
e.preventDefault();
fetch(
"https://****",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
}
).then(response => response.json())
.then(parsedJSON => {
parsedJSON.results.map((
user
) => ({
username: "BotResponse",
content: `${user.message}',
img: "http://***.jpg"
}))
}
)
.then(chats =>
this.setState({
chats
})
)
.catch(error => console.log("parsing failed", error));
//for the sake of readability and debugging, you might want to store
that obj in a local variable here and put that variable in place of ...[]
this.setState(
{
chats: [
...this.state.chats,
{
username: "clientUser",
content: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>,
img: "http://***.jpg"
}
]
},
() => {
ReactDOM.findDOMNode(this.refs.msg).value = "";
}
);
}
Back-end
You want to confirm that this is actually saving to your db (may seem obvious but double check that it is saving the way you want and that you are returning the correct info. in the correct format on your back-end. This could be where the problem is. Log your response to your back-end console and make sure it is returning everything correctly.
Front-End
Look at how you are setting your state in your fetch call. I'm not really sure how you are sending your data but here is an example of a post fetch call using axios.
axios.post("http://****", {
data: this.state.data,
more: this.state.more,
etc: { example: "of", object: this.state.obj }
})
.then(response => {
if (response.status === 200) {
//do something with response.data
//this.setState({ data: response.data})
}
});
There really isn't anything more I can suggest. I'm not really sure what you are trying to achieve with the source you have provided.
Note
There are a few things that you are doing throughout your code that I'm not sure you need to do. But, I did not address that in this answer because I'm not entirely sure what your project is and your desired results. But this should help with the issue of nothing rendering. If it doesn't, look into how you are setting your state in the fetch api call.
Hope this helps.
EDIT
I don't believe you need to have three .then in your api call. Once you have parsed your json you can set your state there.
EDIT 2
The above source was not giving the desired result. The fetch api call was not returning data to the second then call because the data from the first then call. Here is the updated submitMessage function.
submitMessage(e) {
e.preventDefault();
fetch(
"https://****",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
}
).then(response => {
return response.json() //here was the problem, nothing was being returned
})
.then(parsedJSON => {
parsedJSON.results.map(user => ({
username: "BotResponse",
content: user.message, //this shouldn't need to be a string literal
img: "http://***.jpg"
})
)
}
)
.then(chats =>
this.setState({
chats
})
)
.catch(error => console.log("parsing failed", error));
}
I am trying to fetch some data from an API through a POST request, then I would like to display those data on the browser, but I am encountering this problem:
Line 30: Expected an assignment or function call and instead saw an expression
My code looks like this:
import React, { Component } from "react";
import axios from "axios";
class GetData extends Component {
componentDidMount() {
axios
.post(
`https://api.multicycles.org/v1?access_token=API_KEY`,
{
query:
"query ($lat: Float!, $lng: Float!) {vehicles(lat: $lat, lng: $lng) {id type attributes lat lng provider { name }}}",
variables: { lat: 52.229675, lng: 21.01223 }
}
)
.then(response => {
console.log(response);
this.setState({
data: response.data
});
})
.catch(error => {
console.error(error);
});
}
render() {
console.log(this.state.data);
return (
<ul className="filter-options">
{this.state.data.data.map(val => {
<p>{val.templateFields}</p>;
})}
</ul>
);
}
}
export default GetData;
I would like then, to render in App.js a component called <GetData /> and display the results from the API, but I'm still getting the error previously mentioned.
Where I'm doing something wrong?
I'm not sure where you're getting
this.state.data.data
from. You're setting
this.setState({
data: response.data
});
so I would expect something like
this.state.data.map(...)
I also don't see a constructor for your component - you need to set default, state, for example
constructor(props) {
super(props);
this.state = {date: new Date()};
}
You're not returning anything in your map try :
<ul className="filter-options">
{this.state.data.data.map(val => (
<p>{val.templateFields}</p>
))}
</ul>
Also you need a default state in your component.
I have ajax functions in an api to which I'm making an axios get request from my react js component. How can I access the returned data to display it on the web page.
Depends of what you trying to do but this is a example.
componentDidMount() {
axios
.get(`endpoint`)
.then(res => this.setState({ posts: res.data }))
.catch(err => console.log(err))
}
A good way too should be if you using react-router to make the ajax call with the onEnter api from the router.
Here is one way of doing it with React and ES2015.
You will want to set the default state in the constructor and make your get request like in the example below. Just switch the names around to make it work with your application.Then map over the array you get back from the response of the get request. Of course change the names and stylings to suit your needs, I'm using Bootstrap to make things easy to understand. Hope this helps.
import React, { Component } from 'react'
import axios from 'axios';
import cookie from 'react-cookie';
import { Modal,Button } from 'react-bootstrap'
import { API_URL, CLIENT_ROOT_URL, errorHandler } from '../../actions/index';
class NameofClass extends Component {
constructor(props) {
super(props)
this.state = {
classrooms: [],
profile: {country: '', firstName: '', lastName: '', gravatar: '', organization: ''}
}
}
componentDidMount(){
const authorization = "Some Name" + cookie.load('token').replace("JWT","")
axios.get(`${API_URL}/your/endpoint`, {
headers: { 'Authorization': authorization }
})
.then(response => {
this.setState({
classrooms:response.data.classrooms,
profile:response.data.profile
})
})
.then(response => {
this.setState({classrooms: response.data.profile})
})
.catch((error) => {
console.log("error",error)
})
}
render () {
return (
<div className='container'>
<div className='jumbotron'>
<h1>NameofClass Page</h1>
<p>Welcome {this.state.profile.firstName} {this.state.profile.lastName}</p>
</div>
<div className='well'>
{
this.state.classrooms.map((room) => {
return (
<div>
<p>{room.name}</p>
</div>
)
})
}
</div>
</div>
)
}
}
export default NameofClass