I am building an app that relies on some data I need to fetch from my server. Right now my app starts with fetching the data and then passing down the data through several steps which are separated by classes. I simplify the code in the following.
Class1:
export default class Class1 {
constructor(props) {
this.props = props
}
async init() {
const data = await this.fetchData()
new Screen({ data }).init()
}
async fetchData() {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open("POST", 'http://localhost:8888/getData', true)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.onload = () => resolve(JSON.parse(xhr.responseText))
xhr.onerror = reject
xhr.send(JSON.stringify({}))
})
}
}
Class2:
export default class Screen {
constructor(props) {
this.props = props
}
async init() {
let ScreenType
if (this.offscreenIsSupported()) ScreenType = Offscreen
else ScreenType = Onscreen
new ScreenType(this.props).init()
}
offscreenIsSupported() {
return "OffscreenCanvas" in window && "transferControlToOffscreen" in this.props.canvas
}
}
OnscreenClass:
export default class Onscreen {
constructor(props) {
this.props = props
}
async init() {
new Handler(this.props}).init()
}
}
OffscreenClass:
export default class Offscreen {
constructor(props) {
this.props = props
}
async init() {
this.worker = new Worker(new URL('./offscreen.worker.js', import.meta.url))
const offscreen = this.props.canvas.transferControlToOffscreen()
this.worker.postMessage({
type: "init",
canvas: offscreen
}, [offscreen])
}
}
offscreen.worker.js
self.onmessage = msg => {
switch (msg.data.type) {
case 'init':
init(msg.data)
break
}
}
async function init(data) {
self.vgHandler = new Handler({ data })
await self.vgHandler.init()
}
Class3:
export default class Handler {
constructor(props) {
this.props = props
}
async init() {
this.setup(this.props.data)
}
}
As you can see the main code called in Class3 will be the same. This Class needs the fetched data. Class2 is just necessary to determine if the browser should use offscreen or onscreen canvas. Since checking the ability of using offscreen canvas as well as creating the web worker is not dependent to the fetched data it is not very smart to await the fetching process.
How am I able to fetch the data in background, run my code of Class2 simultaneously and use the fetched data in Class3?
I want to make this process as fast as possible.
Best
Related
Here is my simplified setup
//--- sample.com app ---//
async fetchFromServer() {
// return promise..
}
function App() {
const [sampleState, setSampleState] = React.useState(null)
React.useEffect(() => {
fetchFromServer().then((data) => {
setSampleState(data)
})
}, [])
return (
<p>{JSON.stringify(sampleState)}</p>
);
}
//--- android app ---//
public class SampleWebActivity extends Activity {
SamleWebInterface mWebInterface;
WebView mWebView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWebView = new WebView(this);
setContentView(mWebView);
mWebInterface = new SamleWebInterface();
mWebView.addJavascriptInterface(mWebInterface, "Native");
//...
mWebView.loadUrl("https://sample.com");
}
}
// Somewhere in another activity
private void showWebScreen() {
Intent intent = new Intent(this, SampleWebActivity.class);
// use existing activity if present
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
}
//--- Question ---//
How can I re-fetch sampleState from server every time SampleWebActivity is shown without reloading the whole page?
I want to aviod reloading because the actual web app is much bigger than the sample. Also I don't know the exact state of the web app so it's not clear which url to load.
I want to show whetever whas shown before the activity was switched but with updated data.
I'm aware of WebView.evaluateJavascript() but don't know how to interact with the react app after it's compiled into vanilla js.
Using the Webview (browser) itself:
You could just use the webview's built in event system, i.e. When the webview returns from being backgrounded for instance you can leverage the document.visibilityState API (https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState):
async fetchFromServer() {
// return promise..
}
function App() {
const [sampleState, setSampleState] = React.useState(null)
React.useEffect(() => {
const fetchData = () => {
if (document.visibilityState !== "hidden") {
fetchFromServer().then((data) => {
setSampleState(data)
})
}
}
fetchData();
document.addEventListener("visibilitychange", fetchData);
return () => document.removeEventListener("visibilitychange", fetchData);
}, [])
return (
<p>{JSON.stringify(sampleState)}</p>
);
}
Injecting/Calling from Java:
async fetchFromServer() {
// return promise..
}
function App() {
const [sampleState, setSampleState] = React.useState(null)
React.useEffect(() => {
const fetchData = () => {
fetchFromServer().then((data) => {
setSampleState(data)
})
}
/**
* Make some globals
*/
window.__triggerRefetch__ = fetchData;
window.__setState__ = setSampleState;
fetchData();
return () => {
/**
* Remove the globals
*/
delete window.__triggerRefetch__;
delete window.__setState__;
}
}, [])
return (
<p>{JSON.stringify(sampleState)}</p>
);
}
You trigger a refetch with WebView.evaluateJavascript("window.__triggerRefetch__()") or you can inject data with something like WebView.evaluateJavascript("window.__setState__({foo: "bar"})")
i'm trying to get data from the server and write it to a state, but the functions are not firing sequentially
I have a file core.js with with fuctions set
export var LoginAPI = {
signIn: (signInData) => {
request.useQuery(signInData, 'Autorize', 'LogIn').then(Request => {
if (Request.flag) {
return true;
}
else {
GeneralAPI.showNotificationError("Ошибка входа", Request.answer)
return false;
}
})
}
}
request is simple fetch to sigin method into .net core identity
and i have a some page where i need to use it
`class NormalLoginForm extends Component {
//somecode
onFinish = (e) => {
var LoginDatas = {
Login: this.formRef.current.getFieldValue("loginFormItem"),
Password: this.formRef.current.getFieldValue("passWordFormItem")
}
var signIn = core.LoginAPI.signIn(LoginDatas)
this.props.store.isAuthorizeChange(signIn)
console.log("result")
}
render() {
return (
<Form >
<somefFormComponents>
</Form>
);
}
}`
And i have a problem
this function is executed first is this.props.store.isAuthorizeChange(signIn) and only the second one makes a request to the server.
how can this be fixed?
I tried to wrap
var signIn = core.LoginAPI.signIn(LoginDatas)
into Promise but all the same, first changes were called in the storage and only then the resolve method in the promise
I was wondering about the best practice to create an efficient web app. which one of this implementations is more efficient and which one is better in large scale applications?
Every class extends the base request class and has access to request methods:
class BaseRequestClass {
get(url) {
return Promise.resolve() // Some api request
}
}
class Users extends BaseRequestClass {
constructor() {
super()
this.users = []
}
}
class Admins extends BaseRequestClass {
constructor() {
super()
this.admins = []
}
}
const users = new Users();
const admins = new Admins();
users.get('users').then((response) => users.users = response)
admins.get('admins').then((response) => admins.admins = response)
or creating a single instance of BaseRequestClass and use it every time that we want to make a request like this one:
class BaseRequestClass {
get(url) {
return Promise.resolve() // Some api request
}
}
class Users {
constructor() {
this.users = []
}
}
class Admins {
constructor() {
this.admins = []
}
}
const request = new BaseRequestClass()
const users = new Users();
const admins = new Admins();
request.get('users').then((response) => users.users = response)
request.get('admins').then((response) => admins.admins = response)
I prefer to use the first approach, though I would house the specific to each Class within the class instead of passing in as a param.
class BaseRequestClass {
get(url) {
return Promise.resolve() // Some api request, using this.resource
}
}
class Users extends BaseRequestClass {
constructor() {
super()
this.resource = 'users' // <!-- here
this.users = []
}
getAll(){
...
}
}
meaning the usage becomes -
users.getAll()
etc
I've had a good look around but I can't quite construct a search to get any relevant results.
What is the best way to load in a config object that is generated from the server when it isn't possible to call an API?
The config is printed to the page like so:
var config = {configItem: 'configAnswer', ...etc }
Then in react I'm literally reaching in and accessing it as normal
const item = config.configItem;
This may be fine it just doesn't seem very 'react like'.
Any suggestions would be appreciated.
EDIT:
Example of component code. As you can see product.handle is coming from a server generated global variable.
const Cart = class extends React.Component {
constructor() {
super();
this.state = { cartLoaded: false, cart: {} };
}
getCart = async () => {
const cart = await fetch(`/products/${product.handle}.js`);
const json = await cart.json();
this.setState({ cartLoaded: true, cart: json });
};
componentDidMount() {
if (this.state.cartLoaded === false) {
this.getCart();
}
}
render() {
if (this.state.cartLoaded === false) {
return null;
}
return <div className="purchase-container"></div>;
}
};
I want to create ES6 class that reads data from a file and simply returns the content of the file, so I created a class called FileReader which has a constructor filePath and has a method called getFileContent
import fs from 'fs';
import path from 'path';
export class FileReader {
constructor(filePath) {
this.filePath = filePath;
fs.readFile(filePath, (err, data) => {
if (err) {
console.log(err);
}
this.fileContent = data;
});
}
getFileContent(separator, columns) {
console.log(this.fileContent);
}
}
I have a react component called OrderList I want to use FileReader inside componentDidMount method to read the content of the file
import React from 'react';
import {FileReader} from '../Utils/FileReader';
class OrdersList extends React.Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
FileReader reader = new FileReader('');
reader.getFileContent(',' , []);
}
render() {
}
}
export default OrdersList;
the problem that I'm getting an error Unexpected token reader so what's wrong with this approach ?
change this line: FileReader reader = new FileReader(''); to const reader = new FileReader('');
There are two problems in your code:
You're reading file content in the constructor, in the most of the cases, fileContent will be undefined, because fs.readFile is async function.
You're creating a reader without file path: FileReader reader = new FileReader('');
To fix described problems you should move the logic for reading file in class function and use callback or promise:
class OrdersList extends React.Component {
constructor(filePath) {
this.filePath = filePath;
}
getFileContent(separator, columns, cb) {
fs.readFile(this.filePath, (err, data) => {
if (err) {
console.log(err);
}
cb(err, data) ;
});
}
}
In OrdersList you should use real file name and call function with callback to read file content:
class OrdersList extends React.Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
let reader = new FileReader(realFilePath);
reader.getFileContent(',' , [], (err, content) => {
// TODO: file content in content var
});
}
}