D3 Graph displays no graph despite no errors (REACT) - reactjs

Im trying to port the code from https://www.d3-graph-gallery.com/graph/correlogram_scatter.html to react.
Now I know there are multiple things I need to change such that I don't create a new SVG on every new re-render, I just wanted to make sure I could get the example graph working before I started to make changes.
However nothing renders, and the console has no errors which makes debugging this very difficult.
I threw an example into code sandbox that seems to mimic what I see on my end.
What am I missing?
https://codesandbox.io/s/compassionate-haze-9excy?file=/src/App.js

Looks like what this might need to get over the finish line is wrapping our D3 code in React's useEffect hook.
Here's an updated, working Codesandbox.
We can call useEffect initially and we can pass any data we'd like to observe for our chart in the array that useEffect takes as an argument.
This works well because useEffect is called the first time that DOM elements have been rendered, which is why it's the best time to access the DOM element living in the properties of the svg ref.
Every time your component renders, it calls useEffect. You can watch for changes by passing arguments into its array, but if you only want to run useEffect the very first time the component mounts, you can leave that array empty.
In my experience, this has been a helpful pattern:
function YourChartCode({ data, property }) {
const yourRef = useRef();
// will be called initially and on every data change
useEffect(() => {
const svg = d3.select(yourRef.current);
// do D3 stuff here
// leave array empty or pass arguments you're watching into this array
}, [data, dimensions, property])
return (
<svg ref={yourRef}></svg>
)
}

Related

React state change is one step behind

A common problem in React, especially among beginners (like me). This is due to the fact that setState is an asynchronous function and React does not change values one by one, but accumulates some pool if it is not otherwise instructed. (please correct me if the wording is wrong)
So here's the question. One simple solution is to put the desired state into the useEffect dependency array.
For example, I have such a component:
const [first, setFirst] = useState()
const [second, setSecond] = useState()
useEffect(() => {
Something logic by first and second...
}, [first])
Here, the useEffect is executed every time the first state changes. At the same time, at the moment of its operation, the second state is not relevant, next time it will become as it should be now, and so on. Always one step behind.
But if I add a second state to the useEffect dependency array, then everything works as it should.
useEffect(() => {
Something logic by first and second...
}, [first, second])
At the same time, it is not necessary that the useEffect work on changing the second state, I added it only in order to have an up-to-date version inside the useEffect. Can I use the useEffect dependency array this way?
if you use useEffect with an empty array it will act as componentDidMount and componentWillUnmount so it will only invoke itself on first component creation and when it is about to unmount.
https://reactjs.org/docs/react-component.html#componentdidmount
Here I got the problem You are facing, I am not fully sure, but In my case I was console.log(result) withing the fucntion that was changing state, but it was always one step behind. why was that? because in React it is considered as side effect. So If you console.log(result) in useEffect passing the value in dependency array then, it will console log the same value that instantly changed.
In backgorund the state is updating exactly the same time but useEffect detects it as exactly as it is changed.
You can write any logic in useEffect as well or in the function which you are updating.
So there should not be any problem writing logic in the function which you are callilng on click.

React hooks queries

I have the following queries -
What's the difference between useEffect, useMemo and useCallback hooks ? I have gone through many examples and explanations but still am not clear on their difference. All I know is that each executes only when at least one of their dependencies change.
If useRef hook allows us to persist with values between re-renders, why not use a simple variable (not a state) for the same ? I read somewhere that if not changed manually, useRef will have the same value all the time. Can't we achieve this with a simple variable ?
tl;dr
useMemo fired immediately, useCallback not.
local variables a not persisted between renders
Explained
useMemo and useCallback area really very same. The difference between them are mentioned in the hooks names.
useMemo is created for calculating some heavy things (like taking some very long list and mapping it in another) and storing it for some time – as documentation says React can drop useMemo result and make hook to run again. When component is rendering first time, all useMemos continuously runs calculations and their results may be used during the render. When rendering next times (if no hook dependencies changed) React do not call passed function but just using memorized result.
useCallback is created just for preserving variable references to functions that is passed as first argument. It is very helpful when callback, created with that hook is passed to some children components cause persistent variable reference do not invalidates memorized components.
Small example:
const app = () => {
console.log('app render starts')
const title = React.useMemo(() => {
console.log('running calcualtion!')
return 'Hello world'
}, [])
console.log('app render continues')
const handleClick = React.useCallback(() => {
console.log('handling click')
}, [])
console.log('app render continues again')
return <div onClick={handleClick}>{title}</div>
}
/*
Output after mounting app:
- app render starts
- running calcualtion!
- app render continues
- app render continues again
And after clicking div:
- handling click
*/
About useRef
React functional components are functions that runs again on every component render. Without hooks that functions are totally pure and unable to contain any state or preserve variables value – all function-scoped variables are created on every render again.
Hooks know which component are currently being rendered, so hooks able to store some data about component and get it back when component re-rendered.
In lot of cases useRef really are just a way to persist value between renders. As described above, you can't achieve that with simple variables inside of component's function. It could be achieved with some global variable declared outside of component. It even may be better choice if variable value do not depends one component mount/unmount which are handled by useRef.

how to stop react-piano-roll component from duplicating itself on state update

i have a react component in which im using react-piano-roll component, the problem is when i update my component's state, react-piano-roll component replicates itself,
here is link code https://codesandbox.io/s/prod-water-q3eg4p?file=/src/App.js
thank you for your time and suggestions
I have been struggling with this same issue and I had a look into the PianoRoll react component itself. The culprit section seems to be:
useEffect(function () {
container.current.appendChild(pianoRoll.view);
});
After changing the section to read:
useEffect(function () {
container.current.replaceChildren(pianoRoll.view);
});
the component seems to function as intended. From my understanding, useEffect runs on every render which is triggered by setState. As the append code is running each time useEffect is being triggered, the element is being duplicated each time.
My only fix I can see at the moment, unless the base react component is updated, is to make a copy of the component manually and call that instead.
https://codesandbox.io/s/boring-mopsa-cppq2v?file=/src/App.js

useEffect called when a variable not in the dependancy list is updated (causing an infinite loop)

I'm trying to do an ajax call when a state changes, and then set another state to the result of that ajax call.
const [plc, setPlc] = useState(null);
const [tags, setTags] = useState([]);
...
useEffect(()=>{
if(plc != null) {
myAjaxPromiseFunction(plc).catch((err)=>{
console.log(err);
}).then((tags)=>{
setTags(tags);
});
}
}, [plc]);
For some reason, this results in an infinite loop. However, when I remove the setTags(tags); statement it works as expected. It appears that the setTags function is causing the effect hook to update, but tags is not a part of the dependencies of this effect hook.
FYI, the tags variable is supposed to be a list of objects.
EDIT:
I have one other useEffect in my code.
useEffect(() => { // Update which tags are logged
if(plc != null) {
anotherAjaxFunction(plc, tags).catch(err => {
toaster.show({
message: err,
intent: "danger"
});
}).then(tags => {
setTags(tags);
});
}
}, [plc, updateLoggedFlag]);
However, this useEffect is not dependant on tags, and the issue still occurs if I remove this block of code.
Side note: The updateLoggedFlag is a variable I was planning to use to force the effect to update as needed, but I haven't used it anywhere yet.
Another EDIT: Link to reproduction using code sandbox: https://codesandbox.io/s/cranky-ives-3xyrq?file=/src/App.js
Any ideas? Thanks for the help.
In short: move TagColumn outside of App, as it is here.
The problem is not connected to the effects declaration, it's due to the component declaration.
In your sandbox, the TagColumn was defined inside of App. It means that every time App was rendered (in particular, every time its state is changed), the const TagColumn was assigned a different value. React can't see that it is the same component every time - after all, it's not really the same, since the captured scope is changing.
So, App renders a new TagColumn. React sees that component is created from scratch, renders it, and, in particular, invokes its hooks, including every useEffect. Inside useEffect, you use callback to change the state of App. App rerenders, creates yet another component TagColumn, which is again rendered for the first time in its life, calling every useEffect... and the cycle continues.
In general, you shouldn't capture anything non-constant in the functional component scope. Instead:
if the value is generated by external context (App, in this case) - pass it as props;
if the value is generated by the component itself - use useState, useRef or other hooks, as necessary.
The problem I think with your code is that it does not include tags in the dependency array. Conceptually, this should work fine, as mentioned before by other developers, but React documentation states that, all the state variables and props used inside the callback, should be present inside the dependency array, otherwise it can cause bugs. Have a look at it here. Hooks FAQ

How are function calls and component calls different in React?

https://codesandbox.io/s/cocky-silence-486sh?file=/src/App.js
In the above example, we see that calling the ChildWrapper as ChildWrapper() vs <ChildWrapper/> causes some differences.
The function method behaves as intended, so when I increment the counter, the reference stays the same, however using the component call, it actually remounts.
I was wondering if my intuition is correct, that the references are changing, and more important why are they changing. And to add on to that, which should be the preferred method for rendering functions that returns jsx, <Component/> or { Component() }.
When you do <ChildWrapper/> it is translated to React.createElement(ChildWrapper, {}, null) which means you ask react to create an element for you.
When you do ChildWrapper() you are just calling a function, react does not know about this and cannot know about it, so you won't have any of react's features (hooks).
With your example, with logging the two results in the console you will see this, and you can notice that the difference is noticeable:
When calling the function, it will return a <div>, here, jsx will transpiles it to an object with type div.
When calling it via jsx from the start, it will return an object with type ChildWrapper.
PS: you are creating the ChildWrapper inside a component (during render) So it will have a new value each time your Parent component renders, and react after rendering and going into reconcialiation, it will remove the whole previous tree and create a new one, because the type changed.
Let's sum up all what I've wrote:
The child "behaving correctly for you" is the function call, because you basically call it and it returns a div, react after render and during reconciliation, finds a div and then just updates it if there is a change. So the mount useEffect(() => ..., []) will not be executed.
Why the child created with jsx isn't behaving correclty ? because you are dynamically creating its type, so at each render, it will create a new ChildWrapper Type, and react during reconciliation will remove it entirely because its type changed, and thus, the mount useEffect(() => ..., []) will be executed each time, because every time we mount a new element.
Read more about reconciliation in this link
From the official documentation :
Each JSX element is just syntactic sugar for calling
React.createElement(component, props, ...children). So, anything you
can do with JSX can also be done with just plain JavaScript.
Internally, the code generated looks like this: React.createElement(Component, { props }, children),
The createElement function will then register and render the component you gave to it, bind it with the shadow DOM and update it whenever it is necessary. It will also pass its props and children arguments.
But if you call your component directly, none of that happens, your component will not be correctly rendered and updated by react because it lacks the core features given by createElement.

Resources