How can I use unirest API calls in React? - node.js

I'm working with unirest API calls and React for the first time and I'm having trouble implementing a unirest call. While it works in a simple Node.js program, if I try to plug the code below into a React.js file and work with it, I'm unable to get any results for some reason, as I just get an undefined object back.
var unirest = require('unirest');
unirest.get(--insert url here--)
.header("X-Mashape-Key", --insert key here--)
.header("X-Mashape-Host", "spoonacular-recipe-food-nutrition-
v1.p.mashape.com")
.end(function (result) {
console.log(result.status, result.headers, result.body);
});
However, when I plug this into a barebones Node.js file, I get an object back with the values I want. I've been struggling with this for days -- does anyone have any idea what I'm doing wrong?
EDIT: Here is how I tried to implement this in React:
import React from 'react';
import './App.css';
var unirest = require('unirest');
class Kitchen extends React.Component {
callApi() {
unirest.get(--insert api url--)
.header("X-Mashape-Key", --insert api key--)
.header("X-Mashape-Host", "spoonacular-recipe-food-nutrition-
v1.p.mashape.com")
.end(function (result) {
console.log(result.status, result.headers, result.body);
});
render() {
return(
<div className="ingredient-info">
{this.callApi()}
</div>
)
}
EDIT 2: Here is what the expected Object body looks like:
[ { id: 556470,
title: 'Apple fritters',
image: 'https://spoonacular.com/recipeImages/556470-312x231.jpg',
imageType: 'jpg',
usedIngredientCount: 3,
missedIngredientCount: 0,
likes: 243 },
{ id: 73474,
title: 'Apple Turnovers',
image: 'https://spoonacular.com/recipeImages/73474-312x231.jpg',
imageType: 'jpg',
usedIngredientCount: 3,
missedIngredientCount: 0,
likes: 48 },
{ id: 47950,
title: 'Cinnamon Apple Crisp',
image: 'https://spoonacular.com/recipeImages/47950-312x231.jpg',
imageType: 'jpg',
usedIngredientCount: 3,
missedIngredientCount: 0,
likes: 35 } ]

Unirest is for Node (server-side)... Client (browser) has fetch baked in...
Here is a simple get request to https://randomuser.me/ example:
class App extends Component {
state = { users: [] };
componentDidMount() {
fetch("https://randomuser.me/api/?results=10&nat=us")
.then(results => results.json())
.then(data => {
const users = data.results;
this.setState({ users: users });
})
.catch(err => console.log(err));
}
render() {
return (
<div>
{this.state.users.map((user, index) => {
return (
<div key={index}>
<div>{user.name.first}</div>
<img src={user.picture.thumbnail} alt="" />
</div>
);
})}
</div>
);
}
}
Here is a working example of the same: https://codesandbox.io/s/0yw5n3mm7n

Related

Testing a React component that uses redux toolkit and RTKQuery

I have been making an app using redux toolkit and RTKQuery, and hit a stumbling block on how to test a component that uses slices:
Component
export const Status = () => {
const selectedKidId = useSelector(getSelectedKidId);
const { selectedKid } = useGetKidsQuery(undefined, {
selectFromResult: ({ data }) => ({
selectedKid: data?.find((kid: KidType) => kid.id === selectedKidId),
}),
});
return (
<section>
<p>
Active:{' '}
{selectedKidId !== null ? selectedKid?.firstName : 'Select a kid'}
</p>
</section>
);
};
Test
test('title renders as expected', () => {
renderWithProviders(<Status />, {
preloadedState: { kids: { selectedKidId: '0' } },
});
expect(screen.getByText(/Monsters!/i)).toBeInTheDocument();
});
As you see I can add a selectedKidId in the preloadedState but the component also uses a generated hook useGetKidsQuery which return a list of kids, I don't know how or if I can add this to preloadedState as its an apiSlice.
How would I get my list of kids data into this test?

ReactJS Stripe Payments not returning successful message after clicking pay with node and express

I am currently using ReactJS, node, and express with the Stripe Payment API. After clicking the pay button and entering the dummy credit card credentials, the page doesnt process the payment. I have entered the correct publishing key and api key that I got from my dashboard.I believe it likely has somnething to do with what I need to add in the server.js file(aka node backend).I have read through the docs for any clues I can get. Also have searched here on Stack Overflow. None of the questions had the same thing I was looking for. Please see below for pictures and code. Thanks
This is before pressing the button. Please Note the console on the right side.
This is after pressing the button. The loading spinner just displays forever. Also note the console on right side
// Donate.js
import React from "react";
import "./Donate.css";
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
import CheckoutForm from "./CheckoutForm";
// Make sure to call `loadStripe` outside of a component’s render to avoid
// recreating the `Stripe` object on every render.
const stripe = loadStripe(
"pk*****************************"
);
stripe.then((data) => {
console.log(data);
});
const Donate = () => {
return (
<div className="donate">
<h1 className="donate__sectionHeader">Donate Now</h1>
<Elements stripe={stripe}>
<CheckoutForm />
</Elements>
</div>
);
};
export default Donate;
//CheckoutForm
import React, { useState, useEffect } from "react";
import { CardElement, useStripe, useElements } from "#stripe/react-stripe-js";
import "./CheckoutForm.css";
export default function CheckoutForm() {
const [succeeded, setSucceeded] = useState(false);
const [error, setError] = useState(null);
const [processing, setProcessing] = useState("");
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState("");
const stripe = useStripe();
const elements = useElements();
useEffect(() => {
// Create PaymentIntent as soon as the page loads
window
.fetch("/donate", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ items: [{ id: "xl-tshirt" }] }),
})
.then((res) => {
return res.json();
})
.then((data) => {
setClientSecret(data.clientSecret);
});
}, []);
const cardStyle = {
style: {
base: {
color: "#32325d",
fontFamily: "Arial, sans-serif",
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#32325d",
},
},
invalid: {
color: "#fa755a",
iconColor: "#fa755a",
},
},
};
const handleChange = async (event) => {
// Listen for changes in the CardElement
// and display any errors as the customer types their card details
setDisabled(event.empty);
setError(event.error ? event.error.message : "");
};
const handleSubmit = async (ev) => {
ev.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
},
});
if (payload.error) {
setError(`Payment failed ${payload.error.message}`);
setProcessing(false);
} else {
setError(null);
setProcessing(false);
setSucceeded(true);
}
console.log(clientSecret);
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<CardElement
id="card-element"
options={{ hidePostalCode: true, cardStyle }}
onChange={handleChange}
/>
<button disabled={processing || disabled || succeeded} id="submit">
<span id="button-text">
{processing ? <div className="spinner" id="spinner"></div> : "Pay"}
</span>
</button>
{/* Show any error that happens when processing the payment */}
{error && (
<div className="card-error" role="alert">
{error}
</div>
)}
{/* Show a success message upon completion */}
<p className={succeeded ? "result-message" : "result-message hidden"}>
Payment succeeded, see the result in your
<a href={`https://dashboard.stripe.com/test/payments`}>
{" "}
Stripe dashboard.
</a>{" "}
Refresh the page to pay again.
</p>
</form>
);
}
//server.js
const express = require("express");
const app = express();
const { resolve } = require("path");
// This is your real test secret API key.
const stripe = require("stripe")(
"sk_test_**********************************"
);
app.use(express.static("."));
app.use(express.json());
const calculateOrderAmount = (items) => {
// Replace this constant with a calculation of the order's amount
// Calculate the order total on the server to prevent
// people from directly manipulating the amount on the client
return 1400;
};
app.post("/create-payment-intent", async (req, res) => {
const { items } = req.body;
// Create a PaymentIntent with the order amount and currency
const paymentIntent = await stripe.paymentIntents.create({
amount: 1099,
currency: "usd",
// Verify your integration in this guide by including this parameter
metadata: { integration_check: "accept_a_payment" },
});
res.send({
clientSecret: paymentIntent.client_secret,
});
});
app.listen(4242, () => console.log("Node server listening on port 4242!"));
You need to review the server call/network response with the client_secret. The console error indicates you've provided an invalid secret to confirmCardPayment, apparently an empty string.
You specified: .
It would appear that your app is not setting the state via setClientSecret as intended, and you end up with the initial empty string value from useState("");.
Check your client_secret value before the confirmCardPayment call, and step backwards to find where the value is being dropped.

How to render HTML from a prop coming from MongoDB

I can't make my prop render in HTML. I'm making an app for a Christian ministry and I want to be able to post like a blog, I got quill working but I can't show the results rendered, is showing pure HTML.
I'v been trying to follow the rules of react-render-html, but my experience is little, so I don't really know what I'm missing. I try use 'renderHTML' but it doesn't work.
Below is my code, and if you see the screenshot, you will see that the first card is showing the HTML tags.
import React from 'react';
import { Container, Card, Button, CardTitle, CardText, CardColumns, CardSubtitle, CardBody, Collapse } from 'reactstrap';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import { connect } from 'react-redux';
import { getPosts, deletePost } from '../actions/postActions';
import PropTypes from 'prop-types';
import axios from 'axios';
import renderHTML from 'react-render-html';
import PostsForm from './extentions/PostsForm';
class Home extends React.Component {
componentDidMount() {
this.props.getPosts();
}
onDeleteClick = (id) => {
this.props.deletePost(id);
}
constructor(props) {
super(props);
this.onEntering = this.onEntering.bind(this);
this.onEntered = this.onEntered.bind(this);
this.onExiting = this.onExiting.bind(this);
this.onExited = this.onExited.bind(this);
this.toggle = this.toggle.bind(this);
this.state = {
collapse: false,
status: 'Closed',
ButtonText: "Submit Post"
};
}
onEntering() {
this.setState({ status: 'Opening...' });
}
onEntered() {
this.setState({ status: 'Opened' });
}
onExiting() {
this.setState({ status: 'Closing...' });
}
onExited() {
this.setState({ status: 'Closed', ButtonText: "Submit Post" });
}
toggle() {
this.setState(state => ({ collapse: !state.collapse, ButtonText: "Close" }));
}
formOpening = () => {
this.setState({
on: !this.state.on
})
}
render(){
const { posts } = this.props.post;
return(
<Container>
<div style={{float: "left"}}><h5>Current state: {this.state.status}</h5></div>
<div style={{float: "right"}}><Button
color="dark"
style={{marginButtom: '2rem'}}
onClick={this.toggle}>{this.state.ButtonText}</Button></div>
<Collapse
isOpen={this.state.collapse}
onEntering={this.onEntering}
onEntered={this.onEntered}
onExiting={this.onExiting}
onExited={this.onExited}
style={{clear: "both"}}
>
<Card>
<CardBody>
<PostsForm />
</CardBody>
</Card>
</Collapse>
<CardColumns style={{clear: "both"}}>
<TransitionGroup className="Posts">
{posts.map(({ _id, title, subtitle, postbody}) => (
<CSSTransition key={_id} timeout={500} classNames="fade">
<Card>
<CardBody>
<Button className="remove-btn" color="danger" size="sm" onClick={this.onDeleteClick.bind(this, _id)}>×</Button>
<CardTitle><h3>{title}</h3></CardTitle>
<CardSubtitle><h4>{subtitle}</h4></CardSubtitle>
<CardText>{postbody}</CardText>
<Button>Read More</Button>
</CardBody>
</Card>
</CSSTransition>
))}
</TransitionGroup>
</CardColumns>
</Container>
)
}
};
Home.propTypes = {
getPosts: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
post: state.post
});
export default connect(mapStateToProps, { getPosts, deletePost })(Home);
Screenshot of how it looks now
I would like to see that the cards are acting like
Body Text ect etc etc not <p>Body Text ect etc etc</p>
You need to use dangerouslySetInnerHTML API.
From React Docs, slightly modified:
function createMarkup(html) {
return {__html: html};
}
function MyComponent({html}) {
return <div dangerouslySetInnerHTML={createMarkup(html)} />;
}
https://reactjs.org/docs/dom-elements.html

fetch not returning data in react

I'm new to react, i'm having difficulty getting data for a single book out of list, be passed through via axios' get method.
I think it has something to do with the url, but I have been unable to get fix it.
Here's my code:
export function loadBook(book){
return dispatch => {
return axios.get('http://localhost:3000/api/books/book/:id').then(book => {
dispatch(loadBookSuccess(book.data));
console.log('through!');
}).catch(error => {
console.log('error');
});
};
}
//also tried this
export function loadBook(id){
return dispatch => {
return axios.get('http://localhost:3000/api/books/book/' + {id}).then(book => {
dispatch(loadBookSuccess(book.data));
console.log('through!');
}).catch(error => {
console.log('error');
});
};
}
Html code that contains a variable link to each individual book
<div className="container">
<h3><Link to={'/book/' + book._id}> {book.title}</Link></h3>
<h5>Author: {book.author.first_name + ' ' + book.author.family_name}</h5>
<h4>Summary: {book.summary}</h4>
<BookGenre genre={genre} />
</div>
link in Route:
<Route path="/book/:id" component={BookPage} />
Edit: code for the book component
class BookPage extends React.Component{
render(){
const book = this.props;
const genre = book.genre;
console.log(book);
return(
<div>
<div>
<h3> {book.title}</h3>
<h5>Author: {book.author.first_name + ' ' + book.author.family_name}</h5>
<h4>Summary: {book.summary}</h4>
<BookGenre genre={genre} />
</div>
</div>
);
}
}
BookPage.propTypes = {
book: PropTypes.object.isRequired
};
//setting the book with mapStateToProps
function mapStateToProps (state, ownProps){
let book = {title: '', author: '', summary: '', isbn: '', genre: []};
const bookid = ownProps.params._id;
if(state.books.length > 0){
book = Object.assign({}, state.books.find(book => book.id));
}
return {
book: book
};
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(loadBook, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(BookPage);
Instead of doing this:-
axios.get('http://localhost:3000/api/books/book/' + {id})
You should do like this:-
axios.get(`http://localhost:3000/api/books/book/${id}`)
So your action.js might look like this:-
export function loadBook(id){
const request = axios.get(`http://localhost:3000/api/books/book/${id}`);
return dispatch => {
request.then(book => {
dispatch(loadBookSuccess(book.data));
}).catch(error => {
console.log('error');
})
};
}
Since the id, you have passed it seems to be a string so it can be concatenated using ES6 template strings and make sure you wrap your strings in backtick . or you can do it by + operator, also make sure you pass id as a parameter in your loadbook function so that you can join it to your URL.
Figured out the solution to this problem.
My mistake was that I failed to send the id of the item I along with the api call.
Using componentDidMount and sending the dynamic id from the url params solved this problem for me.
Thank you, #Vinit Raj, I guess I was too much of a rookie then.

How can I use Esri Arcgis Map in ReactJs Project?

I'm trying to use Esri map. To include map in my project, here is what I found:
require([
"esri/map",
"esri/dijit/Search",
"esri/dijit/LocateButton",
"esri/geometry/Point",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
But there isn't any esri folder or npm package. Therefore, I'm confused here. How esri is imported in project?
Use esri-loader to load the required esri modules. This is a component rendering basemap.
import React, { Component } from 'react';
import { loadModules } from 'esri-loader';
const options = {
url: 'https://js.arcgis.com/4.6/'
};
const styles = {
container: {
height: '100vh',
width: '100vw'
},
mapDiv: {
padding: 0,
margin: 0,
height: '100%',
width: '100%'
},
}
class BaseMap extends Component {
constructor(props) {
super(props);
this.state = {
status: 'loading'
}
}
componentDidMount() {
loadModules(['esri/Map', 'esri/views/MapView'], options)
.then(([Map, MapView]) => {
const map = new Map({ basemap: "streets" });
const view = new MapView({
container: "viewDiv",
map,
zoom: 15,
center: [78.4867, 17.3850]
});
view.then(() => {
this.setState({
map,
view,
status: 'loaded'
});
});
})
}
renderMap() {
if(this.state.status === 'loading') {
return <div>loading</div>;
}
}
render() {
return(
<div style={styles.container}>
<div id='viewDiv' style={ styles.mapDiv } >
{this.renderMap()}
</div>
</div>
)
}
}
export default BaseMap;
This renders a base map but this is not responsive. If I remove the div around the view div or if I give the height and width of the outer div (surrounding viewDiv) as relative ({ height: '100%', width: '100%'}), the map does not render. No idea why. Any suggestions to make it responsive would be appreciated.
An alternative method to the above is the one demonstrated in esri-react-router-example. That application uses a library called esri-loader to lazy load the ArcGIS API only in components/routes where it is needed. Example:
First, install the esri-loader libary:
npm install esri-loader --save
Then import the esri-loader functions in any react module:
import * as esriLoader from 'esri-loader'
Then lazy load the ArcGIS API:
componentDidMount () {
if (!esriLoader.isLoaded()) {
// lazy load the arcgis api
const options = {
// use a specific version instead of latest 4.x
url: '//js.arcgis.com/3.18compact/'
}
esriLoader.bootstrap((err) => {
if (err) {
console.error(err)
}
// now that the arcgis api has loaded, we can create the map
this._createMap()
}, options)
} else {
// arcgis api is already loaded, just create the map
this._createMap()
}
},
Then load and the ArcGIS API's (Dojo) modules that are needed to create a map:
_createMap () {
// get item id from route params or use default
const itemId = this.props.params.itemId || '8e42e164d4174da09f61fe0d3f206641'
// require the map class
esriLoader.dojoRequire(['esri/arcgis/utils'], (arcgisUtils) => {
// create a map at a DOM node in this component
arcgisUtils.createMap(itemId, this.refs.map)
.then((response) => {
// hide the loading indicator
// and show the map title
// NOTE: this will trigger a rerender
this.setState({
mapLoaded: true,
item: response.itemInfo.item
})
})
})
}
The benefit of using esri-loader over the approach shown above is that you don't have to use the Dojo loader and toolchain to load and build your entire application. You can use the React toolchain of your choice (webpack, etc).
This blog post explains how this approach works and compares it to other (similar) approaches used in applications like esri-redux.
You don't need to import esri api like you do for ReactJS. As the react file will finally compile into a js file you need to write the esri parts as it is and mix the ReactJS part for handling the dom node, which is the main purpose of ReactJS.
A sample from the links below is here
define([
'react',
'esri/toolbars/draw',
'esri/geometry/geometryEngine',
'dojo/topic',
'dojo/on',
'helpers/NumFormatter'
], function(
React,
Draw, geomEngine,
topic, on,
format
) {
var fixed = format(3);
var DrawToolWidget = React.createClass({
getInitialState: function() {
return {
startPoint: null,
btnText: 'Draw Line',
distance: 0,
x: 0,
y: 0
};
},
componentDidMount: function() {
this.draw = new Draw(this.props.map);
this.handler = this.draw.on('draw-end', this.onDrawEnd);
this.subscriber = topic.subscribe(
'map-mouse-move', this.mapCoordsUpdate
);
},
componentWillUnMount: function() {
this.handler.remove();
this.subscriber.remove();
},
onDrawEnd: function(e) {
this.draw.deactivate();
this.setState({
startPoint: null,
btnText: 'Draw Line'
});
},
mapCoordsUpdate: function(data) {
this.setState(data);
// not sure I like this conditional check
if (this.state.startPoint) {
this.updateDistance(data);
}
},
updateDistance: function(endPoint) {
var distance = geomEngine.distance(this.state.startPoint, endPoint);
this.setState({ distance: distance });
},
drawLine: function() {
this.setState({ btnText: 'Drawing...' });
this.draw.activate(Draw.POLYLINE);
on.once(this.props.map, 'click', function(e) {
this.setState({ startPoint: e.mapPoint });
// soo hacky, but Draw.LINE interaction is odd to use
on.once(this.props.map, 'click', function() {
this.onDrawEnd();
}.bind(this));
}.bind(this))
},
render: function() {
return (
<div className='well'>
<button className='btn btn-primary' onClick={this.drawLine}>
{this.state.btnText}
</button>
<hr />
<p>
<label>Distance: {fixed(this.state.distance)}</label>
</p>
</div>
);
}
});
return DrawToolWidget;
});
Below are the links where you can find information in detail.
http://odoe.net/blog/esrijs-reactjs/
https://geonet.esri.com/people/odoe/blog/2015/04/01/esrijs-with-reactjs-updated

Resources