How can I resize elements dynamically while using react? - javascript

I have a grid of components with 3 per row.
They are divs which represent a product and have inner components such as price and description. These products sometimes have longer titles which push the other components downward.
This is fine, but when it happens I want the titles for the other components in the same row to have a height the same, so that the next components (price, rating) are vertically aligned. So the price for each product in a row will be the same.
Then, I want to make the height of the row the max height of the three elements in the row.
Is there a good way I can manipulate the height of elements dynamically which will work with react?

I would inject a function in all child components which is called after the child component is rendered.
Example:
var Product = React.createClass({
componentDidMount: function() {
var height = 200; // calculate height of rendered component here!
// set height only one time
if (this.props.findHeight) {
this.props.setHeight(height);
}
},
render: function() {
return <div style={{height:this.props.height,backgroundColor:'green'}}>
Product
</div>;
}
});
var Main = React.createClass({
getInitialState: function() {
return {
maxHeight:0,
findHeight: true
}
},
componentDidMount: function() {
this.setState({
maxHeight: this.state.maxHeight,
findHeight: false
});
},
setMaxHeight: function(maxHeight) {
if (maxHeight > this.state.maxHeight) {
this.setState({maxHeight: maxHeight})
}
},
render: function() {
return (<div>
<Product setHeight={this.setMaxHeight} height={this.state.maxHeight} findHeight={this.state.findHeight} />
</div>);
}
});
The logic how to calculate the actual height of a component is a different question. You can solve it e.g. with jQuery (How to get height of <div> in px dimension). Unfortunately I can not answer the question for vanilla js, but I am sure that you find the answer very fast when you ask google :-)
Greetings

By extending the answer provided by CapCa, you can get the actual height of the component by Element.getBoundingClientRect().
let domRect = element.getBoundingClientRect();
You can also target the top element of your targeted react component by using React Ref. Your code could be like this,
let domRect = this.TargetedElementRef.getBoundingClientRect(),
elementHeight = domRect.height,
elementWidth = domRect.width; // you can get the width also

Related

React How to get width and height of props.chidren component?

I want to create npm module for react. In this module i need to know height and width of components that passed into my component as props.children. I don't have control over that children. Currently i do that
{React.cloneElement(this.props.children, { ref: this.myRef })}
inside render and then in other function i can get width and height this way
let myRect = this.myRef.current.getBoundingClientRect();
if (!this.state.childSize.height && myRect.height !== 0) {
var cSize = { height: myRect.height, width: myRect.width };
}
but it only works if children is passed like a dom element
<MyModuleComponent>
<div>Hello world!</div>
</MyModuleComponent>
and didn't work if children is passed as a react component
<MyModuleComponent>
<SomeChildComponent/>
</MyModuleComponent>
in this case this.myRef.current doesn't have getBoundingClientRect function.
How can i get width and height of children in this case?

Get rendered element height before it shows up on screen (ReactJS)

I have an animated component that slides up/down depending on the prop (true or false). I'm using maxHeight: 0 to hide the component (transition is being done with CSS) and that's the default state that's being passed as prop. For the opened style I use a maxHeight much bigger than needed just to make sure the content will fit properly. After it's opened I'm able to get its height by ref and set the maxHeight accordingly.
export default class AnimatedInput extends React.Component {
constructor (props) {
super(props);
this.state = {
height: 600
}
}
componentDidUpdate(prevProps) {
var height = this.refs.inputNode ? this.refs.inputNode.clientHeight : height;
console.log(height);
if (this.props.open === false && prevProps.open === true) {
this.setState({height: height});
}
}
render () {
var {height} = this.state;
let test = this.props.open ? 'boxVisible' : 'boxHidden';
var styles = {
boxHidden: {
...
maxHeight: 0,
},
boxVisible: {
....
maxHeight: height,
}
}
return (
<div style={styles[test]} ref="inputNode">
{this.props.children}
</div>
)
}
}
There are 2 problems with this approach:
The first time it's opened and closed is not smooth due to maxHeight being larger than it should (maybe render the opened one off-screen and get its height first?)
If it's closed before fully opened the height will be lower than it should (I suppose it's an easy fix - just need to stop updating the height value).
Am I on the right track? How would you fix these? Should I stick to CSS or maybe make the transition entirely in JS. Thanks for your suggestions!
You're looking for ReactCSSTransitionGroup. I used this for the exact same thing you are.

Get React.refs DOM node width after render and trigger a re-render only if width has value has changed

I'm attempting to get the width of a ref DOM element and set state to then use within the Component render. The problem comes because this width changes on user input and when I try setState within componentDidUpdate it starts an infinite loop and my browsers bombs.
I created a fiddle here http://jsbin.com/dizomohaso/1/edit?js,output (open the console for some information)
My thinking was;
Component Mounts, setState: refs.element.clientWidth
User inputs data, triggers render
shouldComponentUpdate returns true only if new.state is not equal to old.state. My problem is, I'm not sure where makes sense to update this state?
Any help will be much appreciated, thanks for reading!
Brad.
var component = React.createClass({
componentDidMount: function() {
//Get initial width. Obviously, this will trigger a render,
//but nothing will change, look wise.
//But, if this is against personal taste then store this property
//in a different way
//But it'll complicate your determineWidth logic a bit.
this.setState({
elWidth: ReactDOM.findDOMNode(this.refs.the_input).getBoundingClientRect().width
})
},
determineWidth: function() {
var elWidth = ReactDOM.findDOMNode(this.refs.the_input).getBoundingClientRect().width
if (this.state.elWidth && this.state.elWidth !== elWidth) {
this.setState({
elWidth: elWidth
})
}
},
render: function() {
var styleProp = {}
if (this.state.elWidth) {
styleProp.style = { width: this.state.elWidth };
}
return (
<input ref="the_input" onChange={this.determineWidth} {...styleProp} />
)
}
})
I like to use .getBoundingClientRect().width because depending on your browser, the element might have a fractional width, and that width will return without any rounding.

How to position a React component relative to its parent?

I have a parent React component that contains a child React component.
<div>
<div>Child</div>
</div>
I need to apply styles to the child component to position it within its parent, but its position depends on the size of the parent.
render() {
const styles = {
position: 'absolute',
top: top(), // computed based on child and parent's height
left: left() // computed based on child and parent's width
};
return <div style={styles}>Child</div>;
}
I can't use percentage values here, because the top and left positions are functions of the child and parent's widths and heights.
What is the React way to accomplish this?
The answer to this question is to use a ref as described on Refs to Components.
The underlying problem is that the DOM node (and its parent DOM node) is needed to properly position the element, but it's not available until after the first render. From the article linked above:
Performing DOM measurements almost always requires reaching out to a "native" component and accessing its underlying DOM node using a ref. Refs are one of the only practical ways of doing this reliably.
Here is the solution:
getInitialState() {
return {
styles: {
top: 0,
left: 0
}
};
},
componentDidMount() {
this.setState({
styles: {
// Note: computeTopWith and computeLeftWith are placeholders. You
// need to provide their implementation.
top: computeTopWith(this.refs.child),
left: computeLeftWith(this.refs.child)
}
})
},
render() {
return <div ref="child" style={this.state.styles}>Child</div>;
}
This will properly position the element immediately after the first render. If you also need to reposition the element after a change to props, then make the state change in componentWillReceiveProps(nextProps).
This is how I did it
const parentRef = useRef(null)
const handleMouseOver = e => {
const parent = parentRef.current.getBoundingClientRect()
const rect = e.target.getBoundingClientRect()
const width = rect.width
const position = rect.left - parent.left
console.log(`width: ${width}, position: ${position}`)
}
<div ref={parentRef}>
{[...Array(4)].map((_, i) => <a key={i} onMouseOver={handleMouseOver}>{`Item #${i + 1}`}</a>)}
</div>
The right way to do this is to use CSS. If you apply position:relative to the parent element then the child element can be moved using top and left in relation to that parent. You can even use percentages, like top:50%, which utilizes the height of the parent element.

Why doesn't React cache html elements of child components?

Hiello!
I'm wondering whats wrong in the React example bellow or if React works differently than I thought?
I'm looking for a way to reuse the underlying html element for a child react component, when the parents are two different components.
In the example bellow, I would like the inside the Circle component to have the same element after renderC1 and renderC2 is called. For instance so that I could apply a transition css property to animate the color switch, like they would if I e.g. just changed the style directly on the element.
When I render the bellow, React always seems to generate different HTML elements, ref, key or id on the DIV (in the render function of Circle) doesn't help much.
So my questions: is it possible to get React to just reuse the DIV that gets rendered via C1 when C2 is rendered? I thought this was how React should work, optimizing the underlying HTML elements?
Something like:
var C1 = React.createClass({
render: function () {
return (
<Circle background="deeppink" onClick={renderC2}/>
);
}
});
function renderC1 () {
React.render(
<C1 />,
document.getElementById('mount-point'));
}
var C2 = React.createClass({
render: function () {
return (
<Circle background="salmon" onClick={renderC1}/>
);
}
});
function renderC2 () {
React.render(
<C2 />,
document.getElementById('mount-point'));
}
var Circle = React.createClass({
styler: {
width: "100px",
height: "100px",
mozBorderRadius: "50%",
webkitBorderRadius: "50%",
borderRadius: "50%",
background: 'hotpink'
},
componentWillMount: function() {
if (this.props && this.props.background &&
this.props.background !== this.styler.background) {
this.styler.background = this.props.background;
}
},
render: function() {
return (
{/* tried adding key, ref and id, but does not reuse element */}
<div onClick={this.props.onClick} style={this.styler}></div>
);
}
});
renderC1();
This is impossible. The DOM does not allow one element to be in two places at once. Attempting to put a DOM element in a new location will automatically remove it from the old location.
You can see that here. (or more visually, here)
var parent1 = document.createElement('div'),
parent2 = document.createElement('div'),
child = document.createElement('div'),
results = document.createElement('span');
document.body.appendChild(results);
parent1.appendChild(child);
results.textContent += child.parentNode === parent1; //true
parent2.appendChild(child);
results.textContent += ', ' + (child.parentNode === parent1); //false

Categories