What is the correct way of setting React State with Oboe.js? - reactjs

I am new to both React-JS and Oboe.js. I am trying to speed up loading of some JSON data by using Oboe to stream the results. Unfortunately I am unable to do an update state in the function block. So I try to call another function that does the stateSet. Below is a method I have tried but doesn't work. It errors out a mapping function that uses search-results to render it in a table.
var that = this;
oboe({
url: //url,
method: 'POST', // optional
body: //POST-DATA, // optional
})
.on('node', '*', function(things){
that.updateState(things);
// This callback will be called everytime a new object is
// found in the foods array.
console.log( 'Go eat some', things.id);
});
updateState = (props) => {
this.setState({search-result: props});
}
What I am not sure about is the right way of updating a state with oboe.js and React?
Is there a better library to use for streaming JSON data into React?

Recommended approach
If you have the ability to change things server-side, then I would not recommend using Oboe for this. Oboe is useful if your only alternative is to load a large JSON object and you would like to access that data before the whole thing can be parsed.
The best way to optimize loading a lot of data on a client is to send less data at a time and to make multiple requests. A web-socket is the best approach, and Socket.io is a good tool for doing that.
If you need to use Oboe
I'm working to put together an example of oboe.js + react for you to look at, though it's tricky as much of the activity of Oboe happens outside the React lifecyle. I'll update this answer with that example 👍

Related

How to POST form data in React without external API

I am working with Amazon's Mechanical Turk, trying to build an ExternalQuestion site which POSTs data back to AMT using a form-- this is the typical method of passing back answers from an ExternalQuestion, mandated by their API.
Very specifically, I am trying to do this in ReactJS, because it has simplified every other aspect of this project.
Is it possible to get React to POST form data without using an external back-end like Flask/python?
This is an important requirement because as far as I can tell from this information (and my own wasted time), using Flask/python will make the POST data look like it is coming from my server, rather than the Worker's browser, and will get rejected.
And yet, when I look through the React documentation on forms I don't even see a discussion of form methods, like GET and POST. I understand that React is going to want this handled by something like an onClick() function, but I can't see any way to do this in React without making the data look like it's coming from my server rather than Worker's browser.
Your best shot is to use the built in JavaScript Fetch API with the FormData interface.
For the body you can pass in the payload what you can generate with the FormData interface; MDN's documentation on it:
The FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
Don't use XMLHttpRequest, fetch is the newer that is built on that.
A Generic example with fetch would look like the following:
const formData = new FormData();
formData.append('username', 'abc123');
formData.append('foo', 'bar);
fetch('https://example.com/foo/bar', {
method: 'POST',
body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', JSON.stringify(response)))
This fetch call then can be called based on a user action like onClick.
Form post is just XHR POST with form-data or x-www-form-urlencoded body and get text/HTML returned. This can be done on React with Axios.
This answer show using Axios to send form-data -> https://stackoverflow.com/a/47630754/3849555

Global variables in React

I know Redux solves this but I came up with an idea.
Imagine I have an app that gets some JSON on start. Based on this JSON I'm setting up the environment, so let's assume the app starts and it downloads an array of list items.
Of course as I'm not using Redux (the app itself is quite simple and Redux feels like a huge overkill here) if I want to use these list items outside of my component I have to pass them down as props and then pass them as props again as deep as I want to use them.
Why can't I do something like this:
fetch(listItems)
.then(response => response.json())
.then(json => {
window.consts = json.list;
This way I can access my list anywhere in my app and even outside of React. Is it considered an anti-pattern? Of course the list items WON'T be changed EVER, so there is no interaction or change of state.
What I usually do when I have some static (but requested via API) data is a little service that acts kind like a global but is under a regular import:
// get-timezones.js
import { get } from '../services/request'
let fetching = false
let timez = null
export default () => {
// if we already got timezones, return it
if (timez) {
return new Promise((resolve) => resolve(timez))
}
// if we already fired a request, return its promise
if (fetching) {
return fetching
}
// first run, return request promise
// and populate timezones for caching
fetching = get('timezones').then((data) => {
timez = data
return timez
})
return fetching
}
And then in the view react component:
// some-view.js
getTimezones().then((timezones) => {
this.setState({ timezones })
})
This works in a way it will always return a promise but the first time it is called it will do the request to the API and get the data. Subsequent requests will use a cached variable (kinda like a global).
Your approach may have a few issues:
If react renders before this window.consts is populated you won't
be able to access it, react won't know it should re-render.
You seem to be doing this request even when the data won't be used.
The only downside of my approach is setting state asynchronously, it may lead to errors if the component is not mounted anymore.
From the React point of view:
You can pass the list from top level via Context and you can see docs here.
Sample of using it is simple and exists in many libraries, such as Material UI components using it to inject theme across all components.
From engineering concept of everything is a trade of:
If you feel that it's gonna take so much time, and you are not going to change it ever, so keep it simple, set it to window and document it. (For your self to not forget it and letting other people know why you did this.)
If you're absolutely certain they won't ever change, I think it's quite ok to store them in a global, especially if you need to access the data outside of React. You may want to use a different name, maybe something like "appNameConfig"..
Otherwise, React has a feature called Context, which can also be used for "deep provision" - Reference

React Load Data From DB & Store in State Only Once

So here's my situation. I have a component that loads some data from the database, like so:
const task= fetch(`http://example.com`)
.then(result => result.json())
.then(result => {
this.setState({
items: result
})
});
addTask(task);
Now, if I add this code to the componentWillMount method, it does work however, every time I load the component, this method will fire and I don't want it calling the database every time. I want it called once on first load and then stored in the state and never called again.
And because I am rendering a list of items, I have to manually set this items variable in the constructor to avoid null errors:
constructor() {
super();
this.state = {
items: [],
}
}
My question is, how can I load this data only once and then set it to the items state variable. I have tried adding all this code to the reducer of the component (I am using redux), but it doesn't work.
There are several ways of achieving what you are trying to do, but every each of them brings up more questions to answer I think.
First question is, lets say you stored your data locally after your first fetch, whats gonna happen if the data on the server changes? You can somehow notify the client side that data is change and then fetch the data again to sync local data. Or maybe you can periodically check for the new data. There are lots of ways to go.
This situation is exists if you load the data on a parent component and pass the data to the component that is gonna use it or use persistent redux or similar solution.
You can go with a 3 library solution like Realm, Firebase or similar. These systems have persistent or local storage options that will reduce the download when you make call to data. But these solutions bring up the need for changing your back-end logic.
Like I said before there is no single way to handle these situations and it really depends on your project and preference.

How to make the UI respond to subscriptions in Apollo

I'm using react-apollo as a client to communicate with a GraphQL server that I created. I managed to successfully get subscriptions working with the data.subscribeToMore() function as detailed in the Apollo documentation and the up-to-date data shows up when I run my web application inside of two windows. What I'm trying to do is make it so that an notification alert gets displayed when another client changes data that I'm currently looking at so that I can tell that something changed in case I wasn't paying attention? What would be the correct way of doing this?
update method?
updateQueries method?
The dataFromObjectId and refetchQueries fields did not seem relevant for what I was trying to do. Since I'm using redux, is there a way I could dispatch actions directly from my subscription? Would notification alerts be something that I have to use client.subscribe() with?
Assuming you're using the latest version of Apollo, you should be handing the component a prop named "updateQuery" that contains logic for handling the data.
http://dev.apollodata.com/react/subscriptions.html#subscribe-to-more
This section goes over what you need to do, but essentially your "updateQuery" function should do the following:
Take in an object of structure argumentName.data which contains the new information.
Adds the new object to the results by creating a new object.
Returns the new results object.
so it might look something like this:
(prev, { subscriptionData }) => {
if (!subscription.data) {
//If no new data, return old results
return prev;
}
var newResults = Object.assign(
{},
prev,
queryName: { [subscriptionData.data, ...prev[queryName]] }
);
return newResults;

ReactJS fetching new data on prop

As a preface, I'm still new to React, so I'm still fumbling my way through things.
What I have is a component that fetches data to render an HTML table. So I call my Actions' fetchData() (which uses the browser's fetch() API) from within componentWillMount(), which also has a listener for a Store change. This all works well and good, and I'm able to retrieve and render data.
Now the next step. I want to be able to fetch new data when the component's props is updated. But I'm not exactly sure what the proper way to do so is. So I have a three part question
Would the proper place to do my fetchData() on new props be in componentWillReceiveProps(), after validating that the props did change, of course?
My API is rather slow, so it's entirely possible a new prop comes in while a fetch is still running. Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Related to the above question, is there a way to ensure only one fetch is running at any time besides having something like an isLoading boolean in my Action's state (or elsewhere)?
Yes, componentWillReceiveProps is the proper place to do that.
Regarding point 2 and 3:
The idea of cancelling the task and maintaining 'one fetch running' seems to be inadequate. I don't think this kind of solution should be used in any system because implementation would limit an efficiency of your app by design.
Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Why don't you let a 'newer fetch' response override an 'old fetch' response?
If you really want to avoid displaying the old response you can implement it simply using a counter of all fetchData calls. You can implement it in this way:
var ApiClient = {
processing: 0,
fetchData: function(){
processing++
return yourLibForHTTPCall.get('http://endpoint').then(function (response)){
processing--
return response
}
},
isIdle: function(){
return processing == 0
}
}
and the place where you actually make a call:
apiClient.fetchData(function(response){
if(apiClient.isIdle()){
this.setState({
})
}
}
I hope yourLibForHTTPCall.get returns a Promise in your case.

Resources