ArcGIS Map and NextJS rendering - reactjs

I have been trying to fix this issue for a while and I haven't found any suitable solution on the net.
I am trying to solve a rendering issue using nextJS and Arcgis JS API. In this codesandbox, I have created a simple app where I am incrementing a counter every 5 seconds and display a map.The counter is an useState hook and it seems that every time it is updated, the map is re render again. How can I render the map a single time.
I have been following this recommendation and the get started page on the NextJS website.I had a read at some questions on stackoverflow as well...
My code to add the map is the following...
const EsriMap = ()=>{
const mapDiv = useRef(null);
useEffect(() => {
console.log("load map")
// Grab the webmap object out of the UseRef() hook
// so that we can mutate it.
const map=new ArcGISMap({
basemap: "osm",
})
// let map = webmap.current;
const view = new MapView({
map,
container: mapDiv.current,
center: [140, -27],
scale: 40000000,
});
// Configure the map...
}, []);
return <div className={styles.mapDiv} ref={mapDiv}></div>
}
export default EsriMap;
I would appreciate if someone can help me to solve this one.

Problem:
I noticed that you are using the counter directly in App.js
This will cause the application to rerender each time the state of the counter changes
Solution:
You may consider moving your counter into a separate component or custom hook to avoid the rerender of all app
Ps: if you did not figure out how to do that I could prepare an example for you
I hope this helps you

Related

How to properly use Pixi.js / Canvas Drawing with Next.js / React?

Context
I like to use pixi.js for a game I have in mind. Over the years I always used React and next.js.
So what I want to do it use pixi.js inside React / Next.
There are 2 packages out there that integrate into react fiber to render component like but they are outdated and give so many errors to me (for example doesn't work with react 18 and also have poor documentations). (#inlet/react-pixi and react-pixi-fiber)
So I decided to go the useRef route.
Here is some code that works fine:
import { useRef, useEffect } from "react";
import { Application, Sprite, Texture } from "pixi.js";
import bunnyImg from "../public/negx.jpg";
const app = new Application({
width: 800,
height: 600,
backgroundColor: 0x5bba6f,
});
const Pixi = () => {
const ref = useRef();
useEffect(() => {
// On first render add app to DOM
ref.current.appendChild(app.view);
// Start the PixiJS app
app.start();
const texture = Texture.from(bunnyImg.src);
const bunny = new Sprite(texture);
bunny.anchor.set(0.5);
bunny.x = 0;
bunny.y = 0;
bunny.width = 100;
bunny.height = 100;
app.stage.addChild(bunny);
return () => {
// On unload stop the application
app.stop();
};
}, []);
return <div ref={ref} />;
};
export default Pixi;
The Problem
The only problem I have with this is, that hot reload doesn't work. So if I change something in the useEffect hook, I have to go into the browser and manually refresh the page. So basically hot reloading doesn't work.
I think since it uses a ref that basically never changes.
The question
Is there a better way of coding pixi.js inside react / next?
It's probably unrelated to react-pixi. It looks like the default behavior of hot reloading as described on the Github repo.
Hooks would be auto updated on HMR if they should be. There is only one condition for it - a non zero dependencies list.
and
❄️ useEffect(effect, []); // "on mount" hook. "Not changing the past"
The code in the question is using such an empty dependency list, so the behavior is as expected.
Please post the full setup including the hot reload configuration if this is not the issue.
If you want Pixi/Canvas gets auto refreshed on the rendering cycle, you probably need to define those values with react states and add those states to the useEffect dependencies
For example:
const [bunnyProps, setBunnyProps] = React.useState({
anchor: 0.5,
x: 0,
y: 0,
width: 100,
height: 100,
})
React.useEffect(() => {
// ...
bunny.x = bunnyProps.x;
bunny.y = bunnyProps.y;
// ...
}, [bunnyProps])

How to implement promise when updating a state in functional React (when using useState hooks)

I know similar questions are bouncing around the web for quite some time but I still struggle to find a decision for my case.
Now I use functional React with hooks. What I need in this case is to set a state and AFTER the state was set THEN to start the next block of code, maybe like React with classes works:
this.setState({
someStateFlag: true
}, () => { // then:
this.someMethod(); // start this method AFTER someStateFlag was updated
});
Here I have created a playground sandbox that demonstrates the issue:
https://codesandbox.io/s/alertdialog-demo-material-ui-forked-6zss6q?file=/demo.tsx
Please push the button to get the confirmation dialog opened. Then confirm with "YES!" and notice the lag. This lag occurs because the loading data method starts before the close dialog flag in state was updated.
const fireTask = () => {
setOpen(false); // async
setResult(fetchHugeData()); // starts immediately
};
What I need to achieve is maybe something like using a promise:
const fireTask = () => {
setOpen(false).then(() => {
setResult(fetchHugeData());
});
};
Because the order in my case is important. I need to have dialog closed first (to avoid the lag) and then get the method fired.
And by the way, what would be your approach to implement a loading effect with MUI Backdrop and CircularProgress in this app?
The this.setState callback alternative for React hooks is basically the useEffect hook.
It is a "built-in" React hook which accepts a callback as it's first parameter and then runs it every time the value of any of it's dependencies changes.
The second argument for the hook is the array of dependencies.
Example:
import { useEffect } from 'react';
const fireTask = () => {
setOpen(false);
};
useEffect(() => {
if (open) {
return;
}
setResult(fetchHugeData());
}, [open]);
In other words, setResult would run every time the value of open changes,
and only after it has finished changing and a render has occurred.
We use a simple if statement to allow our code to run only when open is false.
Check the documentation for more info.
Here is how I managed to resolve the problem with additional dependency in state:
https://codesandbox.io/s/alertdialog-demo-material-ui-forked-gevciu?file=/demo.tsx
Thanks to all that helped.

Make a widget re-render everytime parent website URL changes

I'm building a widget and adding it on a react website. Widget does specific tasks based on the URL.
Let's say, I want widget to appear exactly when URL is www.company.com. But the widget appears on "www.company.com/welcome" as well if the user navigated through history.push/ or replace.
Only when a state Change happens inside widget on www.company.com/welcome, it realizes, "oh I am not supposed to appear here".
I'm quite new to programming. I want the widget to re-render everytime the parent website URL changes(and not wait for any state change inside Widget). How to do this?
Currently, this is how finding current URL. I tried 2 approaches.
In the first approach here, on some state change, widget realises it shouldn't come here.....but it requires that state change.
const Main =() ={
let currentUrl = window.location.href;
}
In the 2nd approach, it doesn't work even after a state change.
useEffect(() => {
let url = window.location.href;
setCurrentURL(url)
},[currentUrl]);
3rd approach suggested by user, behaves exactly like the above issue.
const Main =() ={
const [url, setUrl] = useState(window.location.href);
}
Something interesting : The widget works properly when it is added on a html website when moved between pages or when navigation is through window.location ="/"
Only when Parent website is navigated through history.push or history.replace and widget doesn't seem to render.
If it's a stupid mistake, feel free to let me know. I have wasted too much time on this problem already and would appreciate any sort of help
have you tried looking at useEffect?
DOC: https://reactjs.org/docs/hooks-effect.html
useEffect(() => {
//****DO SOMETHING HERE WITH YOUR WIDGITS HERE****
}, []); //
you are currently storing the
let currentUrl = window.location.href;
in a variable are you sure you don't want to use useState() hook?
import { useState } from "react";
const Main =() ={
const [url, setUrl] = useState(window.location.href);
}

How to fetch previous value (usePrevious hook) of useRef hook

I'm using react and apexcharts to plot data in real time I receive from a bluetooth device.
To achieve smooth animation I had to create a render loop with withing a setTimeout() function which is triggered each second and use a useRef() hook which references the latest received data.
useEffect(() => {
let bioFeedbackDataAnimation: number;
bioFeedbackDataAnimation = window.setInterval(() => {
if (stateRef.current) {
ApexCharts.exec('realtime', 'updateSeries', stateRef.current, true);
}
}, 1000);
return function cleanup() {
window.clearInterval(bioFeedbackDataAnimation);
};
}, []);
This works perfectly and the animation is smooth because time between renders is constant.
However, I now need to find out - inside the animation loop - if the data I'm plotting is the same as the data I plotted one second ago. I tried using the usePrevious hook, but all exemples I found are to fetch the previous value of a useState hook.
Is it possible to fetch the previous useRef.current value of a useRef hook? If so, how?
Thank you!
I couldn't find out how to do this.
The workaround I found consists of: When storing latest data, also store the previous data in another useState hook and then reference it as well using another stateRef hook.

React JS Freezes Browser

I have a React component which has 2000 elements and based on some filter conditions I update my state, which internally causes re-rendering. Everything seems to be working fine. But when I togglefilter from 2000 elements to say 1000 elements and back&forth, the rendering takes a lot of time and sometimes the browser freezes. I did chrome timeline profiling, the major time consuming piece is rendering. Any help would be appreciated.
As suggested by #enjoylife is a great step but what if you have many components structures in your view, that would be very difficult to debug even memoising the component won't be able to subside the continuous or loop rendering.
I learnt this after I ran into strange freezing and weird error that wouldn't stop any time a user logged in on the homepage. Imagine of all screens. Sometimes, you would hardly notice your component re-rending.
Detect your screen/page (loop) re-rendering with console log
const Home = () => {
conso.log('home re-rending')
// some hooks
return <BigComponent />
}
As written above. The logs must not show more than a limited time deemed after a component has mounted. In my case, it's once. But if it is too much(logs) and would certainly freeze your pc. Therefore, follow the below steps carefully and retrace your steps.
Tips and prerequisite before trying out this proposed solution. Please make sure you have style guide setup e.g. Eslint, it's great. In my case, I reproduced the source code with cra, then sorted out the first and last listed problem which I encountered.
Be careful with the use of React hooks such as useEffect especially. Avoid causing a side effect in a component.
In my case, I created a reusable useUpdateEffect hook and what I intend it to solve as par the name was to detect an update of React props or window props, but it backfires, I won't share the code.
Also, do extra check if you passed correct and expected dependencies, on this Eslint deserve an accolade.
Avoid random keys in React list. Use unique and constant keys in a component list as react depend on it to identify each item. According to react library
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity. You may use the item index as a key as a last resort:
Avoid variable name conflict in your reducer and React component. Please consider the use of style guides as your friend to avoid this fall.
I made the stupid mistake to create a Foo class and use in its render function, which also leads to the freezing scene. Write here for anyone who could meet this problem again.follow this thread.
Avoid infinite loops, Imagine rendering a lot of data at a go. this happen
just in case you share my fate, I urge you to check your loops and make sure you do not have a += instead of -= (or vice versa). Those infinite loops can be quite a big pain in the neck.
Keep your reducer as a reducer, Avoid Action creator, an API call in your reducer or using another reducer in your reducer so, for instance, reducerA in reducerB. When you call to update reducerA in reducerB, the update in reducerA would trigger an update in reducerB whereby cause page/screen to re-render multiple times. for example
// this react reducer in my case
// reducer js file - reducerB
const useBusinesses = () => {
// reducerB as discussed above - the loading context
const { loading } = useLoadingContext(); // the culprit
const [data, setData] = useState(initialState); // initial state,
const [state, dispatch] = useReducer(reducer, data);
useEffect(() => setData(state), [state, setData]);
const { businesses, errorMessage } = state;
const setBusinesses = (payload) => dispatch({ type: `${FETCH_BUSINESSES}_SUCCESS`, data: payload });
const setBusinessesError = (payload) => dispatch({ type: `${FETCH_BUSINESSES}_ERROR`, data: payload });
const fetchBusinesses = async (lglt, type = 'food', limit = 12) => {
try {
// update reducerB: triggers multiple update in reducerA while requesting is pending
loading(FETCH_BUSINESSES, true);
const request = await API.businesses.getWithquery(
`long=${lglt[0]}&latt=${lglt[1]}&limit=${limit}&type=${type}`
);
loading(FETCH_BUSINESSES, false);
setBusinesses(request.data);
} catch (err) {
loading(FETCH_BUSINESSES, false);
// if (!err.response) dispatch(alertMessage(FETCH_BUKKAS, true, 'Please check your network'));
setBusinessesError(err.response.data);
}
});
return { businesses, errorMessage, fetchBusinesses };
};
export const [BusinessesProvider, useBusinessesContext] = constate(useBusinesses);
//home js file
Home = () => {
const { fetchBusinesses } = useBusinessContext();
conso.log('home re-rending')
// some hooks
useEffect(() => {
console.log('am i in trouble, yes!, how many troubles')
fetchBusinesses(coordinates)
}, [fetchBusinesses, coordinates])
return <BigComponent />
}
A quick fix is to implement shouldComponentUpdate See the docs, for whichever child component is being rendered ~2000 times.
shouldComponentUpdate: function(nextProps, nextState) {
return this.props.value !== nextProps.value;
}
Another quick check is to ask yourself if your following the convention of using small, stateless children, passing only props. If not, it might be time to refactor.

Resources