首页 > 解决方案 > 有没有更好的方法从子组件调用 ref.current.scrollIntoView() 以滚动到其父组件中的 ref?

问题描述

我正在开发我的第一个 ReactJS/TypeScript 项目。

我要做的就是ref.current.scrollIntoView()从子组件调用以滚动到其父组件中的引用。

我已经在onClick处理程序中使用了一个相当丑陋的 if 语句来让它工作scrollTest(),以保持 TypeScript 的快乐。希望有一种更清洁的方法来做到这一点。

如果有办法避免Sections.displayname明确设置,则可以加分。

import {
  Container,
  Grid,
  Link,
  Typography
} from '@material-ui/core'

import React, { ReactElement } from 'react'

const Section = (
  {
    children
  }: {
    children: React.ReactNode
  }
): ReactElement => {
  return(
    <>
      <Grid container style={{minHeight: "100vh"}}>
        <Grid item>
          {children}
        </Grid>
      </Grid>
    </>
  )
}

const Sections = React.forwardRef<HTMLElement>((_, ref): ReactElement => {
  const scrollTest = (): void => {
    if (ref && "current" in ref && ref.current) {
      ref.current.scrollIntoView()
    }
  }

  const sections = [
    <>
      <Typography paragraph variant="h3">
        <Link onClick={scrollTest}>Scroll to footer</Link>
      </Typography>
    </>
  ].map((section, index) => {
    return(
      <Section key={index}>
        {section}
      </Section>
    )
  })

  return(<>{sections}</>)
})

Sections.displayName = 'Sections'

export default function Test(): ReactElement {
  const footerRef = React.useRef<HTMLElement>(null)

  return (
    <>
      <Container>
        <Sections ref={footerRef} />
        <Grid
          component="footer"
          container
          ref={footerRef}
        >
          <Grid>
            <Typography paragraph>
              Footer
            </Typography>
          </Grid>
        </Grid>
      </Container>
    </>
  )
}

标签: reactjstypescript

解决方案


根据 jtbandes 的评论,我得到以下信息:

import {
  Container,
  Grid,
  Link,
  Typography
} from '@material-ui/core'

import React, { ReactElement } from 'react'

const Section = (
  {
    children
  }: {
    children: React.ReactNode
  }
): ReactElement => {
  return(
    <>
      <Grid container style={{minHeight: "100vh"}}>
        <Grid item>
          {children}
        </Grid>
      </Grid>
    </>
  )
}

const Sections = (
  {
    footerRef
  }: {
    footerRef: React.RefObject<HTMLElement>
  }
): ReactElement => {
  const scrollToFooter = (): void => {
      footerRef?.current?.scrollIntoView()
  }

  const sections = [
    <>
      <Typography paragraph variant="h3">
        <Link onClick={scrollToFooter}>Scroll to footer</Link>
      </Typography>
    </>
  ].map((section, index) => {
    return(
      <Section key={index}>
        {section}
      </Section>
    )
  })

  return(<>{sections}</>)
}

export default function Test(): ReactElement {
  const footerRef = React.useRef<HTMLElement>(null)

  return (
    <>
      <Container>
        <Sections footerRef={footerRef} />
        <Grid
          component="footer"
          container
          ref={footerRef}
        >
          <Grid>
            <Typography paragraph>
              Footer
            </Typography>
          </Grid>
        </Grid>
      </Container>
    </>
  )
}

不知道为什么,但没有forwardRef()安全运算符 ,?现在可以同时使用RefObjectcurrent使if语句变得多余。

删除forwardRef()也消除了displayname明确设置,给予奖励积分的需要。

考虑了 Dennis Matinez 的建议,但将点击处理程序与点击事件的源放在同一组件中对我来说更有意义。


推荐阅读