0

I am trying to get a layout in which the container component dictates the style of the child components, or more specifically the width property of the children's style prop.

TL;DR: How to set children's props based on parent's getBoundingClientRect() for all renders, including the initial one.

I am able to calculate the desired width after the component mounts, using getBoundingClientRect() on the container component and then running it through my calculations, but for the initial render, this results in being a non-existent value, because obviously, it is still not in the DOM.

Example below:

class Container extends Component {
    constructor(props) {
        super(props);
        this.containerRef = React.createRef();
        this.containerWidth = null;
        this.columnWidth= null;
    }

    componentDidMount() {
        this.containerWidth = this.containerRef.current.getBoundingClientRect().width;
        this.columnWidth = this.containerBaseWidth / this.props.columns;
    }

    injectChildrenWidth = () => {
        return React.Children.toArray(this.props.children)
            .map(el => {
                return React.cloneElement(
                    el,
                    { componentWidth: this.columnWidth }
                );
            });
    };

    render() {
        const Columns = this.injectChildrenWidth();
        return (
            <div ref={this.containerRef} >
                {Columns}
            </div>
        );
    }
}

What would be the proper way to allow the parent component to "dictate" the width of property of the child components based on it's own getBoundingClientRect() width? Initial render never has values for the parent because it is not in the DOM, and I need to get the value in order to render children, so I need the values in the render() method.

I am pretty sure that the whole conception is wrong and I should be looking at some different lifecycle method or smthing, just do not know where/what.

1

use state

class Container extends Component {
  constructor(props) {
    super(props);
    this.containerRef = React.createRef();
    this.state = {
      containerWidth: undefined,
      columnWidth: undefined
    };
  }

  componentDidMount = () => {
    let containerWidth = this.containerRef.current.getBoundingClientRect().width;
    let columnWidth = containerWidth / this.props.children.length;
    this.setState({ containerWidth, columnWidth });
  };

  injectChildrenWidth = () => {
    return React.Children.toArray(this.props.children).map(el => {
      return React.cloneElement(el, { componentWidth: this.state.columnWidth });
    });
  };

  render() {
    const Columns = this.injectChildrenWidth();
    return <div ref={this.containerRef}>{Columns}</div>;
  }
}
  • This would cause a re-render, would it not? Is there no way around it? – Dellirium Jun 7 at 12:07
  • Yes it do re-render thing. Other way is you can try react-sizeme, but I think it's still re-render in children component. – vedsmith92 Jun 7 at 12:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.