Publish results of an API call to a collection in Meteor - javascript

I'm attempting to make a simple RSS feed reader that will allow a user to enter a url in a search bar and display the results using Meteor and React. In my current set up, I have a SearchBar component with a function that makes a call to the meteor method on the server. How can I store the return of the API call in a client side collection? I've seen some examples on using publish and subscribe to do this, but haven't been able to follow. My goal is to save this data in a client side collection so I can access it from any components that will need it, and not have to render subsequent components through the SearchBar component's render method. This is how I currently have it set up:
feeds.js
import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
import parser from 'rss-parser';
if(Meteor.isServer) {
Meteor.methods({
getFeed(url) {
this.unblock();
const feed = {
title: '',
entries: []
};
try {
console.log('trying to get url');
const response = HTTP.get(url);
parser.parseString(response.content, function(err, parsed) {
feed.title = parsed.feed.title;
parsed.feed.entries.forEach(function(entry) {
feed.entries.push(entry);
})
});
console.log(feed.title);
return feed;
} catch(e) {
return false;
}
}
});
}
SearchBar.js
import React, { Component } from 'react';
import { Tracker } from 'meteor/tracker';
import FeedList from './FeedList';
export default class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {
results: null,
url: ''
}
}
onSubmit(e) {
const { url } = this.state;
e.preventDefault();
const response = Meteor.call('getFeed', url, (err, res) => {
if(!err) {
this.setState({
results:res.entries
});
console.log(this.state.results);
} else {
console.log(err.reason);
}
});
}
onChange(e) {
this.setState({
url: e.target.value
});
}
render() {
return (
<div>
<form onSubmit={this.onSubmit.bind(this)}>
<input type="text" placeholder="Enter a URL" value={this.state.url} onChange={this.onChange.bind(this)}/>
<button type="submit">Get Feed</button>
</form>
{this.state.results ? <FeedList feedItems={this.state.results}/> : <p>Load a feed</p>}
</div>
);
}
}

Don't get the feed on the server at all. Get it on the client, and save it using a local collection defined like:
let localCollection = new Mongo.Collection(null)
Regarding the comments:
A typical pattern for this is for a cron job to populate a collection that is published to the client and rendered there.
This is going to be way over-engineered for your needs, and it's commonly regarded as a canonically wrong answer.

Related

How to get React.js to accept a POST to a route?

Building my first React.js app and I can't seem to get the app to redirect.
I am using the Twilio Voice TwiML (here) inside my React app. I have the frontend and server.
I can record what is said then transcribe it. Then redirect with an action: to a URL.
Below is my call.js Twilio function (server). The /Omg redirect isn't working.
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
const recipient = event.recipient;
twiml.record({
// transcribeCallback: '/transcription'
action: '/Omg'
});
twiml.hangup();
return callback(null, twiml);
}
Below is my App.js
import React, {Component} from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Omg from './Omg';
import './App.css';
const { Device } = require('twilio-client');
class App extends Component {
constructor(props) {
super(props)
this.state={
identity: '',
status: '',
ready: false
}
this.onChangeUpdateState = this.onChangeUpdateState.bind(this);
this.setup = this.setup.bind(this);
this.connect = this.connect.bind(this);
this.disconnect = this.disconnect.bind(this);
}
componentDidMount() {
const device = new Device();
this.setState({
device: device
});
device.on('incoming', connection => {
// immediately accepts incoming connection
connection.accept();
this.setState({
status: connection.status()
});
});
device.on('ready', device => {
this.setState({
status: "device ready",
ready: true
});
});
device.on('connect', connection => {
this.setState({
status: connection.status()
});
});
device.on('disconnect', connection => {
this.setState({
status: connection.status()
});
});
}
// This method sets the identity of the Twilio Device
// that your device is going to connect to. This
// example uses hardcoded values so that only devices
// with the identities friend1 and friend2 can connect.
// In a production application, this would have to be
// handled in a much different way. Most likely, the
// app would have users who are authenticated with a
// username and password. Their unique username would
// serve as their device’s identity and they would only
// be able to connect to device’s owned by users in their
// friend list.
connect() {
const recipient = this.state.identity === 'friend1' ? 'friend2' : 'friend1';
this.state.device.connect({recipient: recipient});
}
disconnect() {
this.state.device.disconnectAll();
}
setup(event) {
// prevents form submission and page refresh
event.preventDefault();
fetch(`https://blah-service-2000014-dev.twil.io/token?identity=${this.state.identity}`)
.then(response => response.json())
.then(data => {
this.state.device.setup(data.accessToken);
this.state.device.audio.incoming(false);
this.state.device.audio.outgoing(false);
this.state.device.audio.disconnect(false);
})
.catch(err => console.log(err))
}
onChangeUpdateState(event) {
this.setState({
identity: event.target.value
});
}
render() {
return (
<Router>
<div className="App">
{
this.state.ready
? <button className="noselect"
onMouseDown={this.connect}
onMouseUp={this.disconnect}>
Press 2 Talk
</button>
: <div>
<p>Enter your name to begin.</p>
<form onSubmit={this.setup}>
<input
value={this.state.identity}
onChange={this.onChangeUpdateState}
type="text"
placeholder="What's your name?"></input>
<input type="submit" value="Begin Session"></input>
</form>
</div>
}
<p>{ this.state.status }</p>
</div>
<Switch>
<Route path='/Omg' component={Omg} />
</Switch>
</Router>
);
}
}
export default App;
At this point, I am not sure if its a React rookie error or if its something I am doing wrong with Twilio?
React is a client-side application, not an HTTP server. It can't accept a POST request because no request will be made to it in the first place.
You need to write actual server side code to handle this the POST request and then redirect to a URL that serves up your React application to the browser.

React.js: How do I send search query from react.js frontend to express.js backend?

I'm trying to create a simple search bar using react.js which will connect to my express.js back-end which will return the correct data from the database back to the front-end. But I haven't managed to understand how to send my custom research from my search bar in react to my back-end.
This is my Search component so far, it just creates a simple search bar which is supposed to send the query to the back-end:
mport React, { Component } from 'react';
import axios from 'axios';
export default class Search extends Component
{
constructor(){
super()
this.state = {
query: '',
result: [],
filteredResult: []
};
}
getData = () => {
axios.get("/get-data").then(function(response){
this.setState({
result: response.data
})
});
};
handleInputChange = () => (
this.setState({
query: this.search.value
}), () => {
if (this.state.query) {
this.getData()
}
}
);
componentDidMount(){
this.getData();
};
render() {
return (
<form>
<input placeholder="Search for..." ref={input => this.search = input} onChange={this.handleInputChange}/>
<p>{this.state.query}</p>
</form>
);
}
}
I can connect to the get-data rout, but I haven't managed to figure out how to send my search query to it. I've searched for quite a while and every example I've seen used a bunch of different API's from the web which isn't very helpful for my case.
So, how do I receive my search query inside of my back-end? Thanks!
the second param .get() of axios is config which can hold a params object for any query params to add to the request. The keys of that object is the query key and the value of the keys are the values of the query ?searchTerm=searchValue. So it might look something like
getData = () => {
axios.get(
"/get-data",
{
params: {searchTerm: this.state.query}
}
).then(function(response){
this.setState({
result: response.data
})
});
};

React JS + REDUX + JWT Authentication JSON issue

I have tried a lot to get this done but i dont know why the json is not printing on the DIV tag
I am getting a user1 is undefined
i am using my own api to fetch data which is populate in users.
{ success: true, user: "Normal User" }
JSON is as above
got the authorized token and logged in with it successfully but when i get the JSOn i cant show the user in the HTML page
please help!
Thanks in advance
this is my HomePage.jsx
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { userActions } from '../_actions';
class HomePage extends React.Component {
componentDidMount() {
this.props.dispatch(userActions.getAll());
}
render() {
var { user, users1 } = this.props;
console.log(user); // token
console.log(users1); // json with names and all
return (
<div className="col-md-6 col-md-offset-3">
<h1>Hi {users1.user} !</h1>
<p>
<Link to="/login">Logout</Link>
</p>
</div>
);
}
}
function mapStateToProps(state) {
const { users, authentication } = state;
const { user } = authentication;
// var users1 = {"success":true,"user":"Normal User"}
var users1 = users.users
return {
user,
users1
};
}
const connectedHomePage = connect(mapStateToProps)(HomePage);
export { connectedHomePage as HomePage };```
Looks like you didn't parse JSON after the fetch and users1 in your case is a string. Try JSON.parse(users1).

Get data properly from an API with React

I am using an API which contains cards. And to call this API, I am using Axios.
So far so good, but I want to return the deck_id and for some reason it does not work. And I get the error "this.state.card.map is not a function"
Here is my current code:
import React from "react";
import axios from "axios";
const CARD_API = "https://deckofcardsapi.com/api/deck/new/shuffle/";
export default class PersonList extends React.Component {
constructor(props) {
super(props)
this.state = {
card: []
}
}
async componentDidMount() {
const card= await axios.get(CARD_API)
this.setState({ card})
}
render() {
return (
<ul>
{this.state.card.map(card=>
<li>{card.deck_id}</li>
)}
</ul>
)
}
}
In axios you will get data inside response.data , so this is how you will get access to data
try {
const response = await axios.get(MOVIE_API); //<-- This will have a lot more thing apart from data you need
const movie = response.data; //<---- SOLUTION
this.setState({ movie })
} catch (err) {
console.log(err)
}
Note : Always put your async await code within try catch block if
possible

how to update the url after saving a post

I'm building an editor which can save and update post. The problem I'm facing is that after saving the post for the first time, I get a snippetId from the server which I want to show in the url immediately or else my route is still http://localhost:8000/editor and if I hit save button again then it saves a duplicate copy with a different id. I want the editor url to be something like http://localhost:8000/editor/123 after saving for the first time so that when I hit the save button again then it updates the post instead of saving a duplicate copy with a different id. I was wondering how to tackle this problem? can someone help me find a solution for this problem
codesandbox
editor.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { savePost, retrievePost } from "./actions/posts";
class Editor extends Component {
constructor(props) {
super(props);
this.state = {
title: "",
enteredText: ""
};
this.commonChange = this.commonChange.bind(this);
}
commonChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
componentDidMount() {
//Load the snippet
retrievePost(this.props.match.params.snippetId);
}
// Save Snippet
performSave = snippets => {
console.log("save function clicked");
const { enteredText, title } = this.state;
this.props.savePost({
snippetId: this.props.match.params.snippetId, //if the url doesn't change then this also doesn't change so I get duplicate copies
snippetDescription: enteredText,
snippetTitle: title
});
};
render() {
return (
<>
<input
type="text"
id="titletext"
placeholder="Enter title here"
limit-to="64"
className="inptxt"
name="title"
onChange={this.commonChange}
/>
<button className="btn savebtn" onClick={this.performSave}>
Save Snippet
<i className="fas fa-save" />
</button>
<textarea name="enteredText" onChange={this.commonChange} />
</>
);
}
}
const mapStateToProps = state => ({
snippets: state.snippets
});
export default connect(
mapStateToProps,
{ savePost, retrievePost }
)(Editor);
action.js
import { SAVE_POST, UPDATE_POST, RETRIEVE_POST, HOME_LOADED } from "./types";
import axios from "axios";
export const savePost = ({
snippetId,
snippetDescription,
snippetTitle
}) => async dispatch => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
let snippetData = {
title: snippetTitle,
snippetDescription: snippetDescription
};
// --------------------------------------
console.log("in savePost action");
try {
if (snippetId == null) {
const res = await axios.post("/api/savesnippets", snippetData, config);
snippetData.snippetId = res.data; //cause I only get snippetId from the server
dispatch({
type: SAVE_POST,
payload: snippetData
});
} else {
//add snippetId here for update use only --------------------------------------
await axios.post(
"/api/update",
JSON.stringify({ ...snippetData, snippetId }),
config
);
// --------------------------------------
dispatch({
type: UPDATE_POST,
payload: snippetData
});
}
} catch (err) {
console.log(err);
}
};
You can history.push() from react-router for this purpose like:
history.push('/editor' + id_you_get_from_ajax_call);
And use it where you are getting the ajax response, so for every success you will get a new id_you_get_from_ajax_call and it will update the route.
and create a matching route for editor like:
<Route path="editor/:id" component={ YOUR_COMPONENT } />
React-router history.push() Reference

Categories