How can I get the height of an element after it has rendered? - html

I want to get the height after an element is rendered. The element is inside another component than which I want to detect it in.
I know I can do it like this:
const heightOfStepper = document.getElementsByClassName(
"step-indicator"
)?.[0]?.offsetHeight;
const minHeight = useMemo(
() => `calc(${heightOfStepper}px + var(--small-spacing))`,
[heightOfStepper]
);
But when I console.log out the heightOfStepper, the value is 0 the first render, and if a add some code to my IntelliJ and save, then I get the real height of the stepper, since then the stepper is already mounted.
How can I get the height of an element after it has rendered, and the component is inside another component? (So I can not just use useEffect(() => ..., []))

In react you should avoid regular DOM queries (like getElementsByClassName) - use ref instead. What do you mean you cannot use useEffect? You can use it even in deeply nested component. If you want to get height of parent element you will want to use forward ref, e.g. like this:
// parent component
const App = () => {
const ref = useRef(null);
return (
<div ref={ref}>
MyDiv
<Child ref={ref} />
</div>
);
};
// child component
const Child = React.forwardRef((props, ref) => {
useEffect(() => {
console.log(ref.current.offsetHeight);
}, []);
return <p>I'm a child component</p>;
});
If your find yourself in need of passing ref deep through components tree or e.g. between siblings - pass ref through context - that was answered here

Related

React range slider with two handles

I'm trying to make a range slider with two handles. Its main purpose is to filter the array of cars that I fetch from the server. Should I use library for the slider? I managed to almost get it done without any library. Here is the minimal working example:
https://codesandbox.io/s/trusting-poincare-4kzcvy?file=/src/Slider.js
I seem to get it wrong in regard to calculations, since the red part of rail doesn't follow up precisely when dragging a handle, and the min value matches the full max value if dragged to the end.
//destructive props
const RangeSlider = ({ classes, label, onChange, value, ...sliderProps }) => {
//set initial value to 0 this will change inside useEffect in first render also| or you can directly set useState(value)
const [sliderVal, setSliderVal] = useState(0);
// keep mouse state to determine whether i should call parent onChange or not.
// so basically after dragging the slider and then release the mouse then we will call the parent onChange, otherwise parent function will get call each and every change
const [mouseState, setMouseState] = useState(null);
useEffect(() => {
setSliderVal(value); // set new value when value gets changed, even when first render
}, [value]);
const changeCallback = (e) => {
setSliderVal(e.target.value); // update local state of the value when changing
}
useEffect(() => {
if (mouseState === "up") {
onChange(sliderVal)// when mouse is up then call the parent onChange
}
}, [mouseState])
return (
<div className="range-slider">
<p>{label}</p>
<h3>value: { sliderVal }</h3>
<input
type="range"
value={sliderVal}
{...sliderProps}
className={`slider ${classes}`}
id="myRange"
onChange={changeCallback}
onMouseDown={() => setMouseState("down")} // When mouse down set the mouseState to 'down'
onMouseUp={() => setMouseState("up")} // When mouse down set the mouseState to 'up' | now we can call the parent onChnage
/>
</div>
);
};
export default memo(RangeSlider);
I tried it this way but it doesnt add up to the solution when having two handles.

Conditionally make a page read-only using react

I want to create a React webpage that has both editable and read-only versions, the whole page not just a few elements on the page. A version is displayed to the user based on user id and other conditions. How do I do it?
The only straight forward way I know is to create 2 pages one editable and one read-only and based on the condition show the appropriate version (html page) to the user.
Is there a better and smarter way to do this? Like can I create just one page for both versions and toggle the mode based on the condition to the users?
Your question should have provided an example of some code you had tried but based on the description, very rough example below of one of many possible solutions.
Suppose EditView component is your page and you are able to pass a value for permission based on whatever credential you need to apply.
Then you have a component, ExampleField that takes the permission and displays either an input or static text. A collection of multiple of these fields is mapped from a theoretical array of data that you'll have to fetch from somewhere and the fields are returned by the main component.
const EditView = ({permission}) => {
const [editable, setEditable] = useState();
const [values, setValues] = useState([]);
useEffect(() => {
setEditable(permission);
}, [permission]);
useEffect(() => {
//maybe fetch your data from a back end or whatever and assign it to `values`
//on page load
}, [])
const ExampleField = ({permission, val, index}) => {
const handleChange = (e) => {
let vals = [...values];
vals[index] = val;
setValues(vals);
}
return(
<>
{permission
? <input name="example" type="text" defaultValue={val}
onChange={handleChange} />
: <span>{val}</span>}
</>
)
}
const fields = values.map((value, i) => {
return <ExampleField permission={permission} val={value} index={i}/>
})
return(
<>
{fields}
</>
)
}
Most likely, you'll want to break out various field components into their own file and, instead of using useState, you would probably want to explore useContext or useStore type functionality to lift up your state and do all the react things.
*Haven't tested or even compiled this code - for illustration purposes only.

Passing css classes through props to a children component

So, i have a css file with determined classes, lets say (class1,class2,class3,etc) and i need to make a presentational component that passes through props their properties to a child component. if the called name in the child component then the properties of the css class are available to use.
props.children = the component(s) nested inside that component. We can use React.cloneElement() in order to pass properties into the child.
const PresentationalComponent = ({ children }) => {
const hardCodedClassName = '.some-class';
return (
<>
{React.cloneElement(props.children, { className: hardCodedClassName })}
</>
)
}
// Usage
<PresentationalComponent>
<ChildComponent />
</PresentationalComponent>

nodeId to Element handle

Is there some elegant way how to get an element handle from nodeId? I get a list of all nodes (and nodeIds) by
const nodes = await page._client.send("DOM.querySelectorAll", {
nodeId: doc.root.nodeId,
selector: "*"
});
where nodes.nodeIds is a list of integers. And somehow I would like to run
const html = await page.$eval(nodeId, node => node.offsetParent === null);
To check if the element is visible. The problem is that page.$eval expects string (selector) as a first argument.
I could add a custom attribute through DOM.setAttributeValue and then query that through page.$eval but that seems hacky. So is there some more direct way how to go from devtool-protocol nodeIds to Puppeteer's ElementHandle?
So there is a way how to go from Puppeteer's ElementHandle to nodeId.
const element = page.$('a');
const node = await page._client.send("DOM.requestNode", {
objectId: element._remoteObject.objectId
});
// <- { nodeId: 1 }
My original question asks for the other direction (nodeId -> ElementHandle) but I can work with this. It's also more practical to use Puppeteer by default whenever possible.

Trouble understanding JSX spread operator [duplicate]

This question already has answers here:
What are these three dots in React doing?
(23 answers)
Closed 1 year ago.
Given this example code from the React docs:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
I did some looking into what ...props actually evaluates to, which is this:
React.__spread({}, props)
Which in turn evaluates to {foo: x, bar: y}.
But what I'm wondering is, why can't I just do this:
var component = <Component props />;
I don't see understand what the point of the spread operator is.
This helps make your code more succinct - since props is an object, the spread operator takes the properties of the object you pass in and applied them to the component. So the Component will have properties a foo with a value of x and a bar with a value of y.
It would be the same as:
var component = <Component foo={props.foo} bar={props.bar} />;
just shorter
One of the best overviews of how object-rest-spread syntax works with react is published at reactpatterns.com:
JSX spread attributes
Spread Attributes is a JSX feature. It's syntactic sugar for passing all of an object's properties as JSX attributes.
These two examples are equivalent.
// props written as attributes
<main className="main" role="main">{children}</main>
// props "spread" from object
<main {...{className: "main", role: "main", children}} />
Use this to forward props to underlying components.
const FancyDiv = props =>
<div className="fancy" {...props} />
Now, I can expect FancyDiv to add the attributes it's concerned with as well as those it's not.
<FancyDiv data-id="my-fancy-div">So Fancy</FancyDiv>
// output: <div className="fancy" data-id="my-fancy-div">So Fancy</div>
Keep in mind that order matters. If props.className is defined, it'll clobber the className defined by FancyDiv
<FancyDiv className="my-fancy-div" />
// output: <div className="my-fancy-div"></div>
We can make FancyDivs className always "win" by placing it after the spread props ({...props}).
// my `className` clobbers your `className`
const FancyDiv = props =>
<div {...props} className="fancy" />
You should handle these types of props gracefully. In this case, I'll merge the author's props.className with the className needed to style my component.
const FancyDiv = ({ className, ...props }) =>
<div
className={["fancy", className].join(' ')}
{...props}
/>
-- quoted from reactpatterns.com by #chantastic
Another good overview was published on the babeljs blog post React on ES6+ by Steven Luscher:
Destructuring & spread attributes
Often when composing components, we might want to pass down most of a parent component’s props to a child component, but not all of them. In combining ES6+ destructuring with JSX spread attributes, this becomes possible without ceremony:
class AutoloadingPostsGrid extends React.Component {
render() {
const {
className,
...others // contains all properties of this.props except for className
} = this.props;
return (
<div className={className}>
<PostsGrid {...others} />
<button onClick={this.handleLoadMoreClick}>Load more</button>
</div>
);
}
}
-- quoted from "BabelJS.org blog - React on ES6+" by Steven Luscher