I need to set component dimensions depending on parent element. Is it possible to get width and height of parent by ref from virtualDOM before it renders?
Parent
export default class App extends React.Component {
render() {
return (
<div>
<div className={'avocado-container'} ref={(container) => { this.container = container; }}>
<Resizer parent={this} scrollAxis={'y'}>
</Resizer>
</div>
</div>
);
}
}
I need to get the dimensions in constructor of child.
Is is possible with something like this.refs.container.offsetWidth ?
No. Layout dimensions are calculated by the browser, not the Virtual DOM. Either set the dimensions by hand, or use CSS to make your child component fill width and height. You can also read the width and height from the rendered component in componentDidMount and call setState in there, which isn't ideal and will cause a second render, but sometimes needs to be done.
Related
Scenario
I'm working on a ReactJS project that has a lot of re-used components on the page, so many components that it causes some of the css animations to become sluggish. I've found that if I use display:none on components below the page fold that my performance bottlenecks vanish.
Question
Is there a currently library, or a very simple way to accomplish this? Worst case I will write a library for this and put it on github.
Caveats
Using display:none gives an element 0 width / height so I'll need to use a placeholder of an assumed size.
I will "unrender" visible components once they go out of the visible window area
lazy loading libs didn't pan out like I hoped, LazyLoadJS is the most promising and what I would leverage if I ultimately need to write my own solution
My situation is rather unique but I'll break it down to relevant pieces as much as I can:
My application is a ReactJS SPA or Single Page Application (so lots of xhr, async loading)
Using the same scroll area for the entire life cycle (.content-group)
Json from the CMS includes component names and respective data. My "SomeFactory" (not the real name) gets the mapped component name and renders the component to the factory container.
Some Caveats:
forceCheck is what made this work, it reassess where lazy items are within the scroll container
overflow was helpful since I'm using overflow: hidden as part of this element's ancestry.
Code
import LazyLoad, { forceCheck } from 'react-lazyload';
class LazyLoadOptimized extends React.Component {
render() {
return (
<LazyLoad throttle={50} height={200} offset={400} overflow scrollContainer={'.content-group'}>
{this.props.children}
</LazyLoad>
)
}
}
export default class SomeFactory extends Component {
componentDidUpdate() {
forceCheck();
}
}
I have a component where there are draggable components inside with translate applied for the drag&drop and a default translation when it's rendered depending on parent's height and width to keep the ratio.
When I resize the window, the parent component change size so the components inside have to re-render to reset the default translation based on the parent size. I pass the size props to the child's but they do not rerender, I believe this is because the translation is done straight in the style attribute.
So I made a trick to force re-render, I pass the props key to the wrapper where the value is the height multiplicate by a width which makes a unique key, change on resizing and then rerender my components with the right default position. Here is what it looks like :
render() {
const { height, width, frame, elem } = this.state;
const key = height * width;
return (
<WrapperFrame>
<WrapperMediaElem key={frame.id}>
<WrapperElements key={key} className="droppable" size={{height, width}}>
<DraggableElement element={{...elem}} size={{height, width}} />
</WrapperElements>
</WrapperMediaElem>
</WrapperFrame>
);
}
This solution works for me but it looks like a "trick" and maybe not the good approach. I wanted to know if it's the right way or if is there a better solution?
I am attempting to create an easy-to-use UI/UX selection system.
Essentially, on the viewport, I first have 3 "difficulty" components that choose what "difficulty level" you want.
Once clicked, they should then transition to the next selection phase, which would be "male" or "female".
Clicking this button should move/remove the difficulty level component to the left (out of the viewport), and move the sex selection into the viewport.
How would I implement this?
I also plan on implementing a "Go back" feature as well in the future, so that users can go back and forth from Difficulty and Sex components.
My ideas
Once the button is clicked, I just use CSS to transition the difficulty component out of the viewport, and the "sex" component into the viewport. After the transition, I give that component a display: none to remove it completely from the user.
Concerns: This doesn't seem efficient. Should I be removing the component entirely? If so, where does one begin to do that?
Another method would be to remove/add the components as needed...which I believe would require using the ComponentDidMount() and ComponentDidUnmount() functions, and then using the React Transition Group library to somehow transition them out?
Here is an excellent, Picasso-like, hand-drawn example from myself:
There is how to do the transition.
Typically, setting display: none stops any sort of animation, which you don't want to do.
export default class Modal extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return (
nextProps.show !== this.props.show ||
nextProps.children !== this.props.children
);
}
render() {
const { show, children } = this.props;
return (
<div
style={{
transform: show ? 'translateY(0)' : 'translateY(-100vh)',
opacity: show ? 1 : 0,
}}
>
{children}
</div>
);
}
}
Note that returning false from shouldComponentUpdate will stop re-rendering the modal and its children.
The children could be anything or nothing. So when you don't want to show it, render an empty div outside view window is very efficient.
Key takeaway
display: none shouldn't need most of the time. It doesn't support animation, and render function can return <div /> or null.
React components will re-render when received new props or updated state, regardless display type and window position.
Mare sure have a clear understanding on rendering lifecycle, not all mounting/updating functions can setState, and you can stop re-rendering manually.
I am reusing a component inside 4 different components. On window resize, every parent component's width will be different and I want to pass that unique width to the common child component.
I was using:
constructor(private elRef: ElementRef)......
window.onresize = (e) =>{
console.log("issues",this.elRef.nativeElement.parentElement);
}
.....
But this gives me the last parent's width and not 4 different widths.
Any help would be appreciated. Thanks.
I have a complex web page using React components, and am trying to convert the page from a static layout to a more responsive, resizable layout. However, I keep running into limitations with React, and am wondering if there's a standard pattern for handling these issues. In my specific case, I have a component that renders as a div with display:table-cell and width:auto.
Unfortunately, I cannot query with width of my component, because you can't compute the size of an element unless it's actually placed in the DOM (which has the full context with which to deduce the actual rendered width). Besides using this for things like relative mouse positioning, I also need this to properly set width attributes on SVG elements within the component.
In addition, when the window resizes, how do I communicate size changes from one component to another during setup? We're doing all of our 3rd-party SVG rendering in shouldComponentUpdate, but you cannot set state or properties on yourself or other child components within that method.
Is there a standard way of dealing with this problem using React?
The method of lifecycle you probably want is componentDidMount
The elements have already been placed in the DOM and you can get information about them from the component's refs.
Example:
var Container = React.createComponent({
componentDidMount: function () {
var width = this.refs.svg.getDOMNode().offsetWidth;
},
render: function () {
<svg ref="svg" />
}
});
In componentDidMount you can get the window.width or ReactDom.findDOMNode(this).width. Then use the width in state and pass as a prop as needed. You can also set a listener for the resize event.