I want to draw a line between two boxes. So I have two components for the boxes, and I want a third component to render as a line between these two components. I don't know the positions and dimensions of the two boxes until they have completed rendering, since it will depend on the rendering of other components. In order to draw the line, I need the position of both boxes relative to a common parent element. (The line is drawn with absolute positioning though, so will not affect the boxes.)
So the question is, how do I do this in React? If I understand React correctly, information is passed downwards in the tree, but can flow upwards through callbacks. But the components just pass react elements off to the framework and the actual rendering happens in the background. So how do I access the result of rendering, e.g. the final coordinates of the DOM element?
Furthermore, if I pass this information (the coordinates of the boxes) upwards to a common ancestor and store it in state, don't I risk an infinite loop where updating these coordinates will cause the components to render again?
(I'm using function components if that matters.)
Edit: I'm not asking for a graph-rending library or anything like that. The boxes and line is just an example. I'm asking how to have a React component render based on the DOM coordinates of other rendered React components.
You could use the useRef hook:
import {useRef, useLayoutEffect} from 'react'
const Page = () => {
const componentRef = useRef()
useLayoutEffect(()=> {
console.log(componentRef.current.clientHeight). // height of ref element
console.log(componentRef.current.clientWidth) // width of ref element
console.log(componentRef.current.offsetTop). // px to top of window from ref element
console.log(componentRef.current.offsetLeft) // px to left of window from ref element
)}
return (
<div ref={componentRef}>
Component
</div>
)
}
with offsetTop and offsetLeft you can get the position since offsetTop = y axis and offsetLeft = x axis
Related
I am designing a 3D scene by react-three fiber, and I want to be able to zoom in on the scene via multiple sources (I'm using a zoom Bar and mouse wheel scrolling for zooming now, but other sources may be added in the future like buttons for zoom in and out). To do so, I am using a global state ZoomAmount variable which is a DOM element, and in the onchange callback of the bar, this variable is changed:
const handleChange = () => {
setZoomAmount(refZoomBar.current.value)
}
then I made a hook for zoomAmount state using the react useEffect, so I can change camera.position.z (changing this makes zoom effect)
useEffect(() => {
camera.position.z = ZoomAmount
}, [ZoomAmount])
The problem is that I want the value of the zoombar to change when I zoom in and out using another input (in this case the mouse wheel scroll,) but when I try to implement this feature in the same way by using hooks, the code enters a loop since these elements are both affected by the same value.
How can I do this without facing this issue?
I have put a running demo of my problem here:
https://codesandbox.io/s/zooming-using-multiple-inputs-lub391
I wasn't able to fix this issue but here is what I have tried:
I implemented two hooks
First, a controls eventlistener to set the ZoomAmount whenever camera changed and second, a hook for zoombar to listen to ZoomAmount value change
controls.current?.addEventListener('change', () => {
ZoomAmount = camera.position.z
})
useEffect(()=>{
refZoomBar.current.value = ZoomAmount
},[ZoomAmount])
the above codes are not included in the Sandbox code since they make the program misbehave
This is where the infinite loop starts since ZoomAmount gets changed if the value of camera.position.z is changed and then camera.position.z gets changed again because it is hooked to ZoomAmount.
I also tried using two different states for ZoomAmount but it only makes the loop bigger
I am building a task manager with React and Framer Motion. I'm animating a slide in transition for a react Portal / modal. The portal has a form in it with multiple input text fields in it, each with their own onChange handlers. However, whenever the onChange handler is called, the animation replays itself. I'm not sure what is the issue.
I've tried to add a repeat value of 0 to the transitions, but there seems to be no change. I'm pretty new to framer motion, so please let me what other details I need to provide.
If you are using animate prop, the component will animate on each render, this is useful only for simple cases, where the component typically renders once on the page. But in your case, this prop will not work since the input field re-renders on each change.
For your use case, it's better to use useAnimation() hook and pass the animationControl as variant prop and fire it manually on the first render only.
useEffect(() => {
if (firstRender) {
animationControl.start();
}
}, [])
In the Parent Component I have some <View/> positioned in a relative way.
In a children Component I need to replicate the rendering of some <View/> in the exact coordinates but this time in an absolute way (in order to put them upside the first ones).
How can I achieve this?
EDIT:
it's a tutorial-like screen, i need to add a View with a transparent background and a button that is present on the parent but it must be of the same size and position from screen top-left corner, independently from the device and screen resolution it must be on the same position.
I am trying to conditionally hide a material ui component.
My first thought was that I'd simply not render the element at all (given I'm not purely hiding it due to 'breakpoint' reasons)... but according to the documentation for the mateiral UI Hidden element here: https://material-ui.com/layout/hidden/
This has the benefit of not rendering any content at all unless the
breakpoint is met.
Score, that sounds great. I could say the Hidden element applied when larger than xs and smaller than lg and it'd have the effect of always hiding the component when applied.
However, when I wrap a component in a hidden element using this code:
let withPaper = <Paper>{this.buildQuestion()}</Paper>;
let withToolTip = withPaper;
if (tooltip != null) {
withToolTip = <Tooltip title={tooltip} enterDelay={500} leaveDelay={200}>{withPaper}</Tooltip>
}
let withHidden = withToolTip;
if (this.props.hidden) {
withHidden = <Hidden xsUp xlDown>{withToolTip}</Hidden>
}
return withHidden;
only the visibility appears to change. It still takes up space on the screen. Look at the following two screenshots. You will notice that the "Station Number" text field shows up when the Hidden is not included and it's invisible when it is included... however, the component is still taking up space in the material-ui Grid.
(ignore the messed up vertical alignment)
Without the Hidden component on the station number:
With the Hidden component:
Based on the documentation, this doesn't appear to be the normal/correct behavior.
How can I get the wrapped component to completely not impact anything on the screen (hopefully while still being able to access it's values and props?)?
Turns out my component was sitting in a grid item (https://material-ui.com/layout/grid/) so, while the Text Field wasn't actually rendering... an empty grid items was.
I have a set of items which are rendered into a list. Each item will have a different height when rendered. I need to know item's height in advance before it's mounted into the DOM. So the container should know height of each item it's rendering before they're rendered :)
Is it possible to calculate the height of React element before inserting it into the DOM, dynamically?
I guess you can't get the elements's height before they are rendered because The DOM API can't give you the height of elements it doesn't even know yet.
Are all the different heights totally random ? If not, you could make a guess based on the props object of each element.