如何在不渲染子组件的情况下根据子组件大小更改 React 父组件大小?

Posted

技术标签:

【中文标题】如何在不渲染子组件的情况下根据子组件大小更改 React 父组件大小?【英文标题】:How to change React parent component size based on child component size without rendering child component? 【发布时间】:2019-04-28 06:14:03 【问题描述】:

我创建了一个 React 组件,它接受任何组件并将其呈现为弹出窗口。 父组件接收要呈现(弹出)的组件。渲染组件在这里是子组件,它使用 react-sizeme 获取其大小并传回父组件。 父组件必须取子组件的尺寸,所以调整它的高度和宽度。这是代码:

class Popup extends React.Component<IPopupProps,IComponent>
    constructor(props:IPopupProps)
        super(props);
        this.state=
            childComponent:this.props.children,
            style:
                height:0,
                width:0
            
             
    
    
    // This function runs two times, before and after rendering child component
    // & so have an improper visualization as the size is changed twice here
    
    public OnSize = (size:any) =>
        const width = size.width +20;
        const height =size.height+20;
        this.setState(
            style:height,
                width             
        )
    

    public render()
        return(
            <div className='popup'>
                <div style=this.state.style className='popup-content'>
                    <a className="close" onClick=this.props.onExit>
                            &times;
                    </a>
                    <this.state.childComponent onSize=this.OnSize/>                    
                </div>
            </div>
        )
    

初始宽度和高度设置为 0。因此无法正确渲染。那么有什么办法可以在父组件获得大小之前隐藏子组件或避免其渲染?

编辑:在渲染子组件之前,我们无法获得大小。那么有什么技巧可以完成这项工作。只需正确弹出一个组件即可。

编辑 2:这是调用 Popup.tsx 并将组件发送为子组件显示的 PropsBuilder.tsx

class PopupBuilder extends React.Component<IPopupBuilderProps, IPopup>
    constructor(props:IPopupBuilderProps)
        super(props);
        this.state = 
            showPopup:false
        
    

    public togglePopup = () =>
        this.setState(
            showPopup:!this.state.showPopup
        )
    

    public render ()
        return(
            <React.Fragment>

                    <button onClick=this.togglePopup>this.props.trigger</button>

                    <React.Fragment>
                        this.state.showPopup?<Popup onExit=this.togglePopup >this.props.component</Popup>:null
                    </React.Fragment>

            </React.Fragment>
            
        )
    


export default PopupBuilder;

【问题讨论】:

shouldComponentupdate 钩子是否适用于这种情况 我同时也在搜索这个。实际上,在渲染之前我们无法获得组件的大小。我猜我们不能使用 shouldComponentUpdate 因为它根本不会被渲染。所以现在的问题是如何正确地将任何组件显示为弹出窗口。 首先将其渲染到屏幕外(即视口之外),获取测量值,然后将其渲染到屏幕上。 @JaredSmith 这个建议确实有效。我将它与舍甫琴科的回答结合起来 @NikhilPatil 它类似于游戏开发中的一个古老的技巧。由于游戏的帧预算只有这么少(16 毫秒),因此您通常会将下一帧“绘制”到内存中的图像缓冲区(快速且便宜),然后将整个内容一次全部交换为屏幕上的内容而不是绘制场景中的每个对象都在递增(缓慢/昂贵)。 【参考方案1】:

实际上,这看起来像是更一般的 DOM/javascript 问题。

考虑这种情况:

const span = document.createElement('span');
span.innerText = 'hello';
span.getBoundingClientRect() // ->  width: 0, height: 0, top: 0, … 

这是一个指标,表明你不知道元素的尺寸,直到它在 DOM 中(Rendered in react);

document.body.appendChild(span);
span.getBoundingClientRect(); // -> width: 50, height: 16,  …

在这种情况下我给你的建议是:

    子组件应接受父组件的属性(函数) 使用 React 的“ref”特性查找子元素的实际尺寸 调用“componentDidMount”中的函数(如果子组件可以动态更改,则使用 componentDidUpdate),将子组件尺寸传递给它。

如果您无权访问子组件。你可以这样包装它:

// Popup.tsx

class Popup .... 
 ...
   render() 
     <Measurer>this.props.children</Measurer>
   

并在其中实现获取维度的逻辑。 Measurer 是 Popup 的直接子代,它们的通信可以由您控制。

【讨论】:

这种方法存在一个问题:Parent 组件(Popup.tsx)从它的主Parent 组件(PopupBuilder.tsx)接收childComponent。所以要渲染的子组件没有在同一个类中导入,所以我无法向子组件发送任何方法我什至尝试将“任何”类型的 childComponent 替换为“React.ComponentClass”以及“React” 。零件'。但它没有用 这个 部分有效。谢谢。在 和我的 popup.tsx 中做了一些额外的更改。并将 的不透明度设置为 0 接受它作为答案

以上是关于如何在不渲染子组件的情况下根据子组件大小更改 React 父组件大小?的主要内容,如果未能解决你的问题,请参考以下文章

VueJS 在不更改父数据的情况下编辑道具的正确方法

如何在不使用服务的情况下将子组件之间的反应式表单数据传递给父组件

如何在不使用角度的ng的情况下将输入值从父组件传递给子组件?

Vue 组件 prop 更改不会触发重新渲染

从 websockets 更新数据时,如何在不连接到每个子组件的情况下更新单个子组件?

在不改变状态、道具或父级的情况下反应子级渲染