请问,react中页面渲染完成的是哪个生命周期呢?

作者站长头像
站长
· 阅读数 17

请问,react中渲染完成的是哪个生命周期呢?我认为是:componentDidMount

  useEffect(() => {
    console.log('mounted') // 挂载后
  }, [])

可以当我在这个生命周期内,document.querySelector查询id的时候:却报错

    useEffect(() => {
        if(props.scrollToIdx) {

            const scrollToIdx = props.scrollToIdx 
            
            console.log("滚动ID:", '#item-' + scrollToIdx!.toString())

            const container = document.querySelector("#container")
            const div: HTMLDivElement | null = document.querySelector('#item-' + scrollToIdx!.toString()) // 这里查询出是null
        
            console.log(container, div)
            if(container) {
                container.scrollTo({
                    top: div!.offsetTop,
                    behavior: 'smooth'
                });
            }
        }

    }, [])

报错信息:

Cannot read properties of null (reading 'offsetTop')
TypeError: Cannot read properties of null (reading 'offsetTop')
    at 

也就是说,没有查询出#item-20,原因是还没有渲染到DOM。

请问这个应该如何进行避免呢?

===

完整代码如下:subContainer.tsx


import React, { useEffect, useState } from 'react'
import { ChangeEvent } from'react';

export type ItemType = {
    type: "property" | "method",
    value: string,
    selected?: boolean    
}

export type SubContainerProps = {
    height?: number,
    title: string,
    data: ItemType[], // 这个是items
    scrollToIdx?: number // 滚动到具体的index的data数组元素
    selected?: boolean // class是否被选中
    
}

export default function SubContainer (props: SubContainerProps){ 

    const [data, setData] = useState<ItemType[]>([])

    // 滚动
    useEffect(() => {
        if(props.scrollToIdx) {

            const scrollToIdx = props.scrollToIdx 
            
            console.log("滚动ID:", '#item-' + scrollToIdx!.toString())

            scrollTo(scrollToIdx)
        }
    }, [props.scrollToIdx])

    const scrollTo = (scrollToIdx: number) => {
        const container = document.querySelector("#container")
        const div: HTMLDivElement | null = document.querySelector('#item-' + scrollToIdx!.toString())
    
        console.log(container, div)
        if(container) {
            container.scrollTo({
                top: div!.offsetTop,
                behavior: 'smooth'
            });
        }
    }


    useEffect(() => {
        setData(props.data)
    }, [props.data])

    

    const itemClick = (item: ItemType, index: number) => {
        console.log(item, index)
    }

    const searchChange = (e: ChangeEvent<HTMLInputElement>) => {
        //console.log(e.target.value)
        const searchValue = e.target.value;
        const filteredData = props.data.filter(item => {
            // 假设您要匹配 item 的某个属性,比如 value
            return item.value.toLowerCase().includes(searchValue.toLowerCase());
        });
        // 在这里根据 filteredData 进行后续的处理,比如更新组件状态或重新渲染相关部分
        console.log(filteredData);

        setData(filteredData)
    }



    return (
        <div style={{
            borderRadius: '8px',  
            border: '2px dashed #333', 
            height: props.height ? props.height : ''
            }}>
            <div style={{
                textAlign: 'left',
                height: '40px',
                display: 'flex',
                alignItems: 'center'
                }}>
                <label style={{
                    float: 'left',
                    color: props.selected ? 'green' : '',
                    fontWeight: props.selected ? 'bold' : ''
                    }}>{props.title}
                </label>
                <input 
                style={{float: 'right', marginLeft: 'auto'}} 
                placeholder='搜索:'
                onChange={searchChange}
                ></input>
            </div>
            <div
                id="container"
                style={{
                    overflow: 'auto',
                    height: props.height? props.height - 40 : ''
                }}  
                >
                {data.map((item, index) => (
                    <div 
                        id={ `item-${index}` }
                        key={`${index}-${item.type}-${item.value} `} 
                        style={{ 
                            float: 'left', 
                            display: 'inline-block', 
                            borderRadius: '6px', 
                            border: '2px solid #000', 
                            margin: '8px', 
                            padding:'4px',
                            backgroundColor: item.selected ? '#39fe40': '' 
                        }}
                        onClick={() => {
                            itemClick(item, index)
                        }}
                    >{item.value}</div>
                ))}
            </div>
        </div>
    )
}

调用:

// 创建的props
 
  //滚动位置
  const [selIdx, setSelIdx] = useState<number>(0)
  
  const props:SubContainerProps = {
    data: [
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'property', value: 'firstName' },
      { type: 'property', value: 'lastName' },
      { type: 'method', value: 'getFullname', selected: true },
    ],
    height: 120,
    title: 'classA',
    selected: true,
    scrollToIdx: selIdx
  }

<SubContainer {...props}></SubContainer>
回复
1个回答
avatar
test
2024-08-11

问题出在这行代码上

useEffect(() => {
  setData(props.data)
}, [props.data])

data 是通过 useEffect 写入的,当第一次 render 的时候 data 还是空,你可以试着把这部分代码放到最前面,就会报错 Cannot read properties of undefined (reading 'map')。解法也很简单:

const [data, setData] = useState<ItemType[]>(props.data);

PS:从提供的代码来看,建议移除 data 这个 state,完全多余。

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容