Client/App.js:
import React, { useState, useEffect } from "react";
import Axios from "axios";
const App = () => {
const [movieName, setmovieName] = useState("");
const [movieReview, setmovieReview] = useState("");
const [getReview, setgetReview] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/api/get", (result) => {
console.log(result.data);
setgetReview(result.data);
});
}, []);
const submitReview = () => {
Axios.post("http://localhost:3001/api/insert", {
movieName: movieName,
movieReview: movieReview
})
.then(() => {
alert("Success");
})
.catch((e) => alert(e));
};
return (
<div className="index">
<h2>movie name</h2>{" "}
<input type="text" onChange={(e) => setmovieName(e.target.value)} />
<h2>movie rating</h2>{" "}
<input type="text" onChange={(e) => setmovieReview(e.target.value)} />
<button onClick={submitReview}>submit</button>
{getReview.map((val) => {
return (
<h1>
Movie name : {val.movieName} Movie review: {val.movieReview}
</h1>
);
})}
</div>
);
};
export default App;
Server/index.js:
const express = require("express");
const mysql = require("mysql");
const cors = require("cors");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(express.json());
const db = mysql.createConnection({
host: "localhost",
root: "root",
password: "",
database: "crudatabase"
});
db.connect((err) => {
if (err) throw err;
});
app.get("/api/get", (req, res) => {
const selectStmt = "SELECT movieName,movieReview FROM movie_review;";
db.query(selectStmt, (err, result) => {
res.send(result);
});
});
app.post("/api/insert", (req, res) => {
const movieName = req.body.movieName;
const movieReview = req.body.movieReview;
const insertStmt =
"INSERT INTO movie_review (movieName,movieReview) VALUES (?,?);";
db.query(insertStmt, [movieName, movieReview], (err, result) => {
console.log(err);
});
});
app.listen(3001, () => {
console.log("Server running on 3001");
});
In the above react and express code I am able to insert the data in the database but after inserting then() part in client is not working. Also the useEffect is not working. I tried many ways but not able to get the reason. I would be glad if someone can solve me the error and all the dependencies are already installed.
In your useEffect, you're passing a callback to Axios.get - this is not consistent with the Axios API (you even do it correctly in the submitReview function!):
useEffect(() => {
// change to Promise.then() chain
Axios.get("http://localhost:3001/api/get").then((result) => {
console.log(result.data);
setgetReview(result.data);
});
}, []);
Your then() chain is not working because your POST response handler never returns a status or a response! Just like in your GET handler, your POST handler needs to let the client know that a request has been successful. e.g. res.send(/*...*/) or even just res.sendStatus(200).
As you are dealing with the promise and have used the thenable syntax while submitting the values but you are not using it while getting the values. try using the below code and check whether this resolves your problem. One more concise method to deal with promises is to use async/await try to use the below code hopes this resolves your problem.
useEffect(() => {
const getMovies = async () => {
try {
let { data } = await Axios.get("http://localhost:3001/api/get");
console.log(data);
setgetReview(data);
} catch (error) {
console.log(error);
}
};
getMovies();
}, []);
Your useEffect is returning a promise try to use async await or .then on your code.
Try to change it from:
useEffect(() => {
Axios.get("http://localhost:3001/api/get", (result) => {
console.log(result.data);
setgetReview(result.data);
});
}, []);
To:
useEffect(() => {
const get_data = async () => {
try {
const result = await Axios.get("http://localhost:3001/api/get")
console.log(result.data)
setgetReview(result.data)
} catch (e) {
console.log(e)
}
}
get_data()
}, []);
Related
I am attempting to retrieve the JSON data stored in the following API:
https://api.hatchways.io/assessment/blog/posts
Using node.js and https requests, I constantly receive an array of [ Promise { }, Promise { } ]. Unfortunately, I can only search by one tag at a time, and I have to retrieve a list of posts that has at least one tag from a list of provided tags, before sorting the posts. My code is below:
const express = require('express');
const app = express();
app.get("/api/ping", (req, res) => {
res.status(200).send("{\"success\": true}");
})
app.get("/api/posts", (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
let posts_array = [];
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
console.log(posts_array);
})
app.listen(3000, () => console.log("Listening on port 3000..."));
function getPost(tag) {
const https = require('https');
return new Promise( (resolve, reject) => {
const options = {
hostname: 'api.hatchways.io',
path: `/assessment/blog/posts?tag=${tag}`
}
let body = [];
const req = https.request(options, res => {
res.on('data', data => {
body.push(data);
});
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch (error) {
reject(error);
}
resolve(body);
});
});
req.on('error', error => {
reject(error);
});
req.end();
}).then(function(data) { return data; }, function(error) { console.log(error) });
}
the getPost method is returning a promise, just do this:
app.get("/api/posts", async (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
const promises = [];
tag_array.forEach(tag => {
promises.push(getPost(tag))
});
posts_array = await Promise.all(promises)
console.log(posts_array)
})
Just wait for all promises to resolve using the await keyword.
app.get("/api/posts", async (req, res) => { // Add the async keyword
//... Skipped some code for concision
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
await Promise.all(posts_array) // Add this to await all promises to resolve
console.log(posts_array);
})
i'm really new to Backend and was trying to fiddle around with API-Calls and Client-Server stuff.
const express = require("express");
const cors = require("cors");
const fetch = require("node-fetch");
const app = express();
app.use(cors());
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
app.get("/express_backend", (req, res) => {
fetch(
"http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=XXXXXXXXXXXXXXXXXX&steamid=76561197996613749&format=json"
)
.then((response) => response.json())
.then((data) => console.log(data))
.then((data) => res.send(data));
});
so this is my server file and it properly fetches the data from the steam api output screenshot
But im struggling to get this from the server to my client.
import React, { useState, useEffect } from "react";
const App = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch("/express_backend")
.then(function (response) {
return response.json();
})
.then(function (json) {
let fetchedData = json;
setData(fetchedData);
})
.catch(function (err) {
console.log(err.message);
});
}, []);
return (
<div>
<p>{data}</p>
</div>
);
};
export default App;
I think that the "Unexpected end of JSON input" Error is related to the "response.json() line but im not really sure how to go on with this or how to fix it. Thanks in Advance!
there is a small issue on the backend server. In the below Promise chain, the second thenable is not returning anything to the third thenable, so basically you are not writing anything to the 'response' when doing res.send(data)
app.get("/express_backend", (req, res) => {
fetch(
"http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=XXXXXXXXXXXXXXXXXX&steamid=76561197996613749&format=json"
)
.then((response) => response.json())
.then((data) => console.log(data))
.then((data) => res.send(data));
});
The correct order would be to merge second and third then handlers, like so
app.get("/express_backend", (req, res) => {
fetch(
"http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=XXXXXXXXXXXXXXXXXX&steamid=76561197996613749&format=json"
)
.then((response) => response.json())
.then((data) => {
console.log(data);
res.send(data);
})
})
Here is my mock file __mocks__/#optimizely/optimizely-sdk.js
const optimizelySDK = jest.requireActual('#optimizely/optimizely-sdk')
optimizelySDK.createInstance().onReady = () => ({ success: false }))
module.exports = optimizelySDK
Here is my test file Optimizely.test.js
import optimizelySDK from '#optimizely/optimizely-sdk'
test('onReady', () => {
const response = optimizelySDK.createInstance().onReady()
expect(response).toBe({ success: false })
})
I think I might be going about this all wrong. This worked perfectly when I try this with lodash. I believe this is because optimizelySDK is a class. I think I should be mocking that instead. How do I successfully mock and test optimizelySDK?
For anyone who came across this on Google, I had the same problem and got it working with jest:
jest.mock('#optimizely/optimizely-sdk', () => ({
...jest.requireActual('#optimizely/optimizely-sdk'),
createInstance: () => ({
getEnabledFeatures: jest.fn().mockReturnValueOnce(['featureA', 'featureB']),
onReady: jest.fn().mockResolvedValueOnce({ status: 200 })
})
}))
describe('my-test', () => {
it('should pass', async () => {
const result = await getFeatures()
console.log(result) // ['featureA', 'featureB']
// assert on result here
});
});
where my code looked something like:
const getFeatures = async (event) => {
try {
const optimizelyInstance = optimizelySDK.createInstance({
sdkKey: process.env.OPTIMIZLEY_SDK_KEY,
});
const optimizelyParameters = {}
return optimizelyInstance.onReady().then(() => {
const result = optimizelyInstance.getEnabledFeatures('id', optimizelyParameters);
return result;
});
} catch (err) {
console.error('Could not get features', err);
}
};
Existing code:
loginUser.js:
import { getUserDetails } from '../api/userDetails';
import { mapApiObjectToModel } from '../mapper/userProfileMapper';
import axios from 'axios';
export const getLoggedInUserDetails = async () => {
axios
.get('/api/getUserDetails')
.then(response => {
return mapApiObjectToModel(response);
})
.catch(err => {
console.log('error==', err);
});
};
userProfileMapper.js:
export const mapApiObjectToModel = inputObj => {
const outputObj = {};
const authorizedRoles = ['Admin'];
if (inputObj) {
outputObj.fullName = '';
if (inputObj.data) {
outputObj.fullName = inputObj.data.data;
}
outputObj.role = 'Admin';
outputObj.isAuthorized = authorizedRoles.includes(outputObj.role);
}
console.log('outputObj', outputObj);
return outputObj;
};
loginUser.test.js:
import axios from 'axios';
import getLoggedInUserDetails from '../../action/loginUser';
jest.mock('axios');
describe('routes using memory router', () => {
it('Get Admin message', async () => {
const data = 'Admin';
axios.get.mockImplementationOnce(() => Promise.resolve(data));
console.log(data);
await expect(getLoggedInUserDetails()).resolves.toEqual(data);
expect(axios.get).toHaveBeenCalledWith('/api/getUserDetails');
});
it('fetches erroneously data from an API', async () => {
const errorMessage = 'Network Error';
axios.get.mockImplementationOnce(() => Promise.reject(new Error(errorMessage)));
await expect(getLoggedInUserDetails()).rejects.toThrow(errorMessage);
});
});
I'm really new to all these, so any assistance would be appreciated. Even any suggestions on TDD for userProfileMapper.js would be appreciated :)
The mapApiObjectToModel returns an object like,
{
role: 'ADMIN',
isAuthorized: true
}
However, in your test, you are expecting it to be equal to a string 'Admin'
const data = 'Admin';
...
await expect(getLoggedInUserDetails()).resolves.toEqual(data); // Note that you have initialize data to 'Admin'
Try changing data to be an object, like,
const data = {
role: 'Admin',
isAuthorized: true
};
...
await expect(getLoggedInUserDetails()).resolves.toEqual(data);
Updated: loginUser.js:
import { getUserDetails } from '../api/userDetails';
import { mapApiObjectToModel } from '../mapper/userProfileMapper';
import axios from 'axios';
export const getLoggedInUserDetails = () => {
return axios
.get('/api/getUserDetails')
.then(response => {
return mapApiObjectToModel(response);
})
.catch(err => {
console.log('error==', err);
throw err;
});
};
You function getLoggedInUserDetails had following issues,
You were not returning the promise from the function.
You don't need async here as you are accessing expect(Promise).resolves in your test file loginUser.test.js:.
You need to throw err from catch block, if you want to test the rejection of promise or remove the catch block.
I have updated following items to the function getLoggedInUserDetails,
removed the async from export const getLoggedInUserDetails = async () => {
returned promise from axios.get('/api/getUserDetails')
added throw err to catch block
You should not mix usage of Promise.then and async/await for more information on the difference between them check here and here
I am new to React and javascript for that matter and am having trouble setting a components state. What I am trying to do is fetch a list of photos from an endpoint of mine. Once I have the photo list I need to map over the photos and call another endpoint to add some other meta data. My goal is to set the state of my component array once so I only render the webpage one time with all of the images.
I believe I need a Promise.All statement to determine when all of the images have returned from a fetch statement inside of a map. However I am not sure if this is the right approach or how it would look.
I will post some of my code below. Any and all recomendations are welcome, Thanks!
server.js
const express = require('express');
const app = express();
var AWS = require('aws-sdk');
var url = require('url');
var http = require('http');
var s3 = new AWS.S3({apiVersion: '2006-03-01'});
var url = require('url');
var http = require('http');
var sizeOf = require('image-size');
var params = {
Bucket: "owenpersonalphotos",
};
const URL = "http://owenpersonalphotos.s3.amazonaws.com/";
app.get('/api/listPhotos', (req,res) => {
console.log("GET /api/listPhotos")
s3.listObjects(params, function (err, data) {
res.json(data.Contents)
});
});
app.get('/api/getPhoto/:key', (req, res) => {
console.log("GET /api/getPhoto/"+req.params.key)
let key = req.params.key;
s3.listObjects(params, function (err, data) {
let imgUrl = URL+key;
options = url.parse(imgUrl);
http.get(options, function (response) {
var chunks = [];
response.on('data', function (chunk) {
chunks.push(chunk);
}).on('end', function() {
var buffer = Buffer.concat(chunks);
res.json(sizeOf(buffer))
});
})
});
});
const port = 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
App.js
class App extends React.Component {
constructor() {
super();
this.loadPhotos = this.loadPhotos.bind(this);
this.state = {
Photos: []
};
}
componentDidMount() {
this.loadPhotos();
}
loadPhotos() {
let p = []
fetch('api/listPhotos')
.then(res => res.json())
.then(data =>
{ Promise.all(data.map(data => fetch("api/getPhoto/" + data.Key)
.then(res => res.json())
.then(getPhotoData => (p = ({
src:"https://owenpersonalphotos.s3.amazonaws.com/"+data.Key,
width: getPhotoData.width,
height : getPhotoData.height,
id: data.ETag
},this.setState({Photos:p}))))))
})
}
render() {
return (
<div className="App">
{this.state.Photos}
</div>
);
}
}
You could try something along the lines of this. Unfortunately I am unable to replicate your exact scenario so it might not work, but here's how you could resolve a Promise.all.
const initialiser = fetch('api/listPhotos')
.then(res => res.json())
.then(data => { // map every photo request to the promise of the fetch
let requests = data.map(data => fetch("api/getPhoto/" + data.Key)
.then(res => res.json())
.then(getPhotoData => ({
src: "https://owenpersonalphotos.s3.amazonaws.com/" + data.Key,
width: getPhotoData.width,
height : getPhotoData.height,
id: data.ETag
)}))
// Promise.all waits until all jobs are resolved
Promise.all(requests)
.then(responses => this.setState({ Photos: responses });
})
})
I would use React Hooks instead for functional components and storing state into an array
import React, { useState, useEffect } from "react";
State set as empty array below:
const [photos, setPhotos] = useState([]);
This following effect will run getPhotos() once on page load
useEffect(() => {
getPhotos();
}, []);
getPhotos from API and map to state photos
const getPhotos = async () => {
//async way
try {
const response = await fetch("YOUR API URL");
let photos = await response.json;
setPhotos(photos);
} catch (err) {
console.log(err);
}
/* with just promises
return fetch("YOUR API URL")
.then(response => response.json())
.then(response => {
console.log(response);
setPhotos(response);
});
*/
and then you can just map it in the return statement:
<div className="photos-container">
{photos.map((p, i) => {
return (
<div className="photo" key={i}>
<img src={photos.imgSrc} alt={photos.name}>
</div>
);
})}
</div>
Hope this helps I know it's not your example exactly
Your fetches were look like a promise hell situation, I will make it a bit clearer like this:
loadPhotos() {
let promises = [];
fetch('api/listPhotos')
.then(res => res.json())
.then(list => {
for (let photo of list) {
const aCall = fetch("api/getPhoto/" + photo.Key).then(res => res.json());
promises.push(aCall);
}
return Promise.all(promises);
}).then(detailedPhotos => {
const Photos = detailedPhotos.map(item => ({
src: "https://owenpersonalphotos.s3.amazonaws.com/" + item.Key,
width: item.width,
height : item.height,
id: item.ETag
}));
this.setState({ Photos });
})
}
render() {
return (
<div className="App">
{this.state.Photos.map(p => <div>{p.id}</div>)}
</div>
);
}