react js / superagent / file dropzone - reactjs

I have a comments widget, in React js (jsx), and I'm using React Dropzone and uploading the dropped files to the server, using superagent.
I need to get the file object (containing my app's file id,etc) returned from my app, and associate them with the comment that the user will submit. I am attempting to assign the file objects to a state variable, 'attachments'. Because of the async nature of superagent, I think, I am actually populating my state variable with an empty array.
I have tried to use a callback, but got an 'undefined' error.
Here is the code:
onDrop: function (newFiles) {
newFiles.forEach((file)=>
{
this.setState({files: this.state.files.concat(file)});
})
var attachments = [];
var req = request.post('/attachments/create');
req.set('Accept', 'application/json');
newFiles.forEach((file)=> {
req.attach('img_attach', file);
req.field('filename', file.name);
req.field('itemType', 'comment');
req.field('itemId', false);
req.end(function(err,res){
var json = $.parseJSON(res.text);
attachments.push(json);
attIds.push(json.id);
});
});
attachments.forEach((file)=>
{
this.setState({
attachments:this.state.attachments.concat([file])});
});
},
Here is the callback attempt which returns "Cannot read property 'setState' of undefined":
function fileAttach(err,res)
{
var json = $.parseJSON(res.text);
this.setState({attachments:this.state.attachments.concat([json])});
}
For the callback, instead of this
req.end(function(err,res){
var json = $.parseJSON(res.text);
attachments.push(json);
attIds.push(json.id);
});
I use this
req.end(fileAttach);
So, one possibility is that I'm looking for a 'context' option, similar to jquery, that allows me to use 'this' in the callback.

So, you were on the right track for the first issue I see. You need to bind the context to that function. It's already been answered by LodeRunner28 in the comments, but you'd do:
req.end(fileAttach.bind(this))
If you're not familiar, Function.prototype.bind allows you to manually force a context variable for any function. It's incredibly handy, and it means you never have to rely on the library (eg. jQuery) to provide a context arg, you can just specify it yourself :)
The bigger issue I see is the way you're using SuperAgent. I believe you're actually sending a whole bunch of requests; calling .end triggers SuperAgent to make the request, and you're doing it inside the forEach loop.
I'm not super familiar with SuperAgent, but I believe you can just do:
newFiles.forEach( file => {
req.attach('img_attach', file);
req.field('filename', file.name);
req.field('itemType', 'comment');
req.field('itemId', false);
});
req.end(fileAttach.bind(this));

Related

i cannot save a response after execute the request using axios Instance (Pokedex - PokeApi)

I trying to do a pokedex using react and pokeAPI but when i try to save the response on a constant and return it this always return undefined.
c
try {
const pokemonResponse = getPokemonByName({
pokemonName,
})
console.log(pokemonResponse)
return pokemonResponse
} catch (e) {
throw new Error("Ups! We had a problem with product's fetch. Details: " + e)
}
}
here a codesandbox with the project
https://codesandbox.io/s/compassionate-haslett-ngv7z?file=/src/data/storage/PokemonStorage.ts
I found 2 issues in the code. See my fork on Codesandbox.
getPokemonByName returned undefined (because it didn't return anything). It should return Promise containing response from API I assume.
Compare it to getAllPokemons function in the same file that actually returns Promise.
Make sure you understand the arrow functions syntax.
Now you need to add await when calling getPokemonByName in async function. This will correctly assign result to pokemonResponse. See docs on async functions.

Figma React plugin PostMessage not working as expected

I'm trying to create a plugin for Figma, which has been going fine until now. It's based on the react example they provide on their github page: https://github.com/figma/plugin-samples/tree/master/react
In this example I've created a button, that on click will call this function:
(file: ui.tsx)
onClick = () => {
parent.postMessage({pluginMessage: {type: 'GetData'}}, '*');
};
This is parent.postMessage is a function figma provides to communicate with another file in the project, code.ts. This file will receive the postMessage with the pluginMessage as parameter, which works as expected. The code.ts that receives this looks like this:
(file: code.ts)
figma.ui.onmessage = msg => {
if (msg.type === 'GetData') {
figma.ui.postMessage({"title": figma.currentPage.selection[0].name});
}
};
This file receives the message, and it gets in the if statement since GetData has been set. Up until here, everything is good and well. The issue I'm walking into is the figma.ui.postMessage({}), which should do a callback to the onmessage function in ui.tsx:
(file: ui.tsx)
onmessage = (selection) => {
console.log(selection);
};
This onmessage function should, according to Figma's documentation, receive the object from the postMessage in code.ts. This does however never happen; it will never be called at all. I can't access the current selection in ui.tsx, so I need data from code.ts. Is there any way to pass this data to ui.tsx, or does anyone know why this doesn't work?
I encountered the same issue. Within your ui.tsx file, try adding the following:
window.onmessage = selection => {
let message = selection.data.pluginMessage;
console.log(message);
}
or try this ->
window.addEventListener("message", (selection) => {
console.log(selection);
});
this will add another message event handler to the window. if you use onmessage it might overwrite the previous handler!
put first of script
onmessage = event => {
console.log("got this from the plugin code", event, event.data.pluginMessage)
}

How to use Meteor.settings in a React Component

I have a React component which is making an API call on init on the client side. I don't want to hard-code my API key (god forbid in the repo), and it's not much better to put it in Meteor.settings.public since that can just be looked up in the console. I want to keep it in Meteor.settings, but then it's invisible to the client. I've tried using a method, but although it appears to work on the server the method call returns undefined on the client.
On the server:
Meteor.methods({
getFileStackAPIKey: function () {
if (Meteor.settings.fileStackAPIKey) {
console.log(Meteor.settings.fileStackAPIKey) // returns: [fileStackAPIKey] correctly
return Meteor.settings.fileStackAPIKey
}
else {
return {message: "Configure Meteor.settings.fileStackAPIKey to connect to FileStack."}
}
}});
On the client:
console.log(Meteor.call('getFileStackAPIKey')); // returns: undefined
I tried to use ReactiveVar and again it set it on the server but was inaccessible on the client. I have the feeling that I'm missing something obvious. Specifically, what I'm trying to make work is FileStack. Their example code shows the API key hard-coded inline. As does the official FileStack React package. This just doesn't seem like a good idea.
It has to do with callbacks. The method result will be in the callback, so what I needed to do on the client was more like this:
Meteor.call('getFileStackAPIKey', (err, res) => {
console.log("FileStack API Key: " + res);
});
But because what I really wanted to do was pass it into the FileStack init (again, on the client side), so I needed to put the following in the constructor for the FileStack object:
// "this" is the FileStack object we're constructing
const fileStackObj = this;
Meteor.call('getFileStackAPIKey', (err, apiKey) => {
// here we're inside the callback, so we have the resulting API key
const client = filestack.init(apiKey, clientOptions);
// these are synchronous actions dependent on the existence of "client"
// that we could not do outside of the callback
fileStackObj.state = {
client,
picker: action === 'pick' ? client.picker({ ...actionOptions, onUploadDone: fileStackObj.onFinished }) : null,
};
fileStackObj.onFinished = fileStackObj.onFinished.bind(fileStackObj);
fileStackObj.onFail = fileStackObj.onFail.bind(fileStackObj);
});

How to call the same api multiple times in Express Route?

I'm working on a Node app with Express. I'm chaining several http calls to data api's, each dependent on the previous req's responses.
It's all working except the last call. The last call needs to happen multiple times before the page should render.
Searching has turned up excellent examples of how to chain, but not make a call to the same API (or HTTP GET, data endpoint, etc.) with different params each time.
I'm trying to do something like this: Using a generator to call an API multiple times and only resolve when all requests are finished?
var getJSON = (options, fn) => {
.....
}
router.route("/")
.get((req, res) => {
var idArray = [];
var results = [];
getJSON({
.... send params here, (result) => {
//add response to results array
results.push(result);
//create var for data nodes containing needed id params for next call
let group = result.groupsList;
//get id key from each group, save to idArray
for(i=0;i<groups.length;i++){
idArray.push(groups[I].groupId);
}
//use id keys for params of next api call
dataCallback(idArray);
});
function dataCallback(myArray){
// number of ID's in myArray determine how many times this API call must be made
myArray.forEach(element => {
getJSON({
.... send params here, (result) => {
results.push(result);
});
// put render in callback so it will render when resolved
}, myRender());
};
function myRender() {
res.render("index", { data: results, section: 'home'});
}
})
I learned the problem with the above code.
You can call functions that are outside of the express route, but you can't have them inside the route.
You can't chain multiple data-dependent calls, not in the route.
Anything inside route.get or route.post should be about the data, paths, renders, etc.
This means either using an async library (which I found useless when trying to build a page from multiple data sources, with data dependent on the previous response), or having an additional js file that you call (from your web page) to get, handle and model your data like here: Using a generator to call an API multiple times and only resolve when all requests are finished You could also potentially put it in your app or index file, before the routes.
(It wasn't obvious to me where that code would go, at first. I tried putting it inside my router.post. Even though the documentation says "Methods", it didn't click for me that routes were methods. I hadn't really done more than very basic routes before, and never looked under the hood.)
I ended up going with a third option. I broke up the various API calls in my screen so that they are only called when the user clicks on something that will need more data, like an accordion or tab switch.
I used an XMLHttpRequest() from my web page to call my own front-end Node server, which then calls the third party API, then the front-end Node server responds with a render of my pug file using the data the API provided. I get html back for my screen to append.
In page:
callFEroutetoapi(_postdata, _route, function (_newdata){
putData(_newdata);
});
function putData(tData){
var _html = tData;
var _target = document.getElementById('c-playersTab');
applyHTML(_target, _html);
}
function callFEroutetoapi(data, path, fn){
//url is express route
var url = path;
var xhr = new XMLHttpRequest();
console.log('data coming into xhr request: ', data);
//xhr methods must be in this strange order or they don't run
xhr.onload = function(oEvent) {
if(xhr.readyState === xhr.DONE) {
//if success then send to callback function
if(xhr.status === 200) {
fn(xhr.response);
// ]console.log('server responded: ', xhr.response);
}
else {
console.log("Something Died");
console.log('xhr status: ', xhr.status);
}
}
}
xhr.onerror = function (){console.log('There was an error.', xhr.status);}
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(JSON.stringify(data));
}
It adds an extra layer, but was necessary to show the latest, frequently changing data. It's also reusable which is better for a multiscreen web app. If there were fewer views (completely different screens and co-dependent datasets), a more centralized model.js file mentioned above would work better.

why does backbone think I'm treating an object like a function?

Adding Backbone to a Rails app, I created a post model inside an app namespace like this
var app = {
this.models.post = new app.Models.Post();
In the router, I created the following route
"posts/:id": "postDetails"
When I navigate to /posts/4, I'm getting an Uncaught TypeError: object is not a function error when I try to call fetch on the model like this
postDetails: function (id) {
console.log(id);
var post = new app.models.post({id: id});
this.post.fetch({
success: function (data) {
$('#content').html(new PostView({model: data}).render().el);
}
});
}
According to the Backbone docs http://backbonejs.org/#Model-fetch, I should be able to call fetch on a model to retrieve the data from the server. Why does Backbone think I'm treating an object like a function?
You're doing this:
this.models.post = new app.Models.Post();
to, presumably, set app.models.post to an instance of the app.Models.Post model. Then you try to do this:
var post = new app.models.post({id: id});
But you can only use the new operator on a function:
new constructor[([arguments])]
Parameters
constructor
A function that specifies the type of the object instance.
You probably want to say:
var post = new app.Models.Post({ id: id });
or something similar.
The problem is you've declared post as a local variable var post, but then tried to access it as a member this.post. You need either this:
this.post = new app.models.post({id: id});
this.post.fetch({ ...
Or this:
var post = new app.models.post({id: id});
post.fetch({ ...
(The difference being that a local variable var post is declared in transient scope and thrown away after postDetails completes; while instance variable this.post gets added to the Router object and will typically live for the whole lifetime of the application.)

Resources