如何使用 React 函数组件获取组件的位置

How to get Position of Components using React Function Components

我正在尝试做这样的事情:

当我点击左边的卡片,然后 ctrl+点击右边的卡片时.. 会画一条线。 Ctrl+再次单击右侧的卡片,该行将被删除。我将使用 svg 路径画线,但我必须获得卡片在 Container Component 中的位置才能这样做。

我正在尝试使用 ref 对 React 函数组件执行此操作,但我不太明白。我知道函数组件没有“实例”,因此 ref 不像 类.

中那样工作

请回答以下问题:

感谢任何帮助,并提前致谢!

编辑

这是我目前拥有的:

容器组件

import React, { useState, useRef } from 'react';

import { Container, Row, Col, Card, Button } from 'reactstrap';
import SectionContainer from './SectionContainer';

const ContainerComponent = (props) => 
{
    const [keyCards, setKeyCards] = useState([
        { id: 1, name: 'key one', description: "this is a key card for one" },
        { id: 2, name: 'key two', description: "this is a key card for two" }
    ])

    const [valueCards, setValueCards] = useState([
        { id: 1, name: 'value one', description: "this is a value card" },
        { id: 2, name: 'value two', description: "this is a value card" },
        { id: 3, name: 'value three', description: "this is a value card" }
    ])

    const [connections, setConnections] = useState([
        // I need something like this for all connections that need a line
        {
            keyID: 1,
            keyPos: { x: 100, y: 200 },
            attachedValues: [
                { valueID: 1, valPos: { x: 200, y: 200 } },
                { valueID: 2, valPos: { x: 200, y: 300 } }
            ]
        }
    ])

    const cardRef = useRef()

    const getPositions = () =>
    {
        console.log(cardRef.current)
        // if I can get positions and if they are selected
        // then I can set the state and generate svg paths
    }

    return (
        <>
            <Row >
                <Col className="display-section" md='6'>
                    <SectionContainer
                        title="Rules for Life"
                        items={keyCards}
                        cardRef={cardRef}
                        onClickHandler={getPositions}
                    />
                </Col>

                <Col className="display-section" md='6'>
                    <SectionContainer
                        title="Entitlements for Days"
                        items={valueCards}
                        cardRef={cardRef}
                        onClickHandler={getPositions}
                    />
                </Col>
            </Row>
            {/* <DrawLines connections={connections} /> */}
        </>
    )
}

export default ContainerComponent 

SectionContainer

import React from 'react';

import { Row, Input, InputGroup, InputGroupAddon, InputGroupText } from 'reactstrap';

import CardContainer from './CardContainer'

const SectionContainer = ({ title, items, cardRef, onClickHandler }) => 
{
    return (
        <>
            <h6>{title}</h6>
            <Row >
                <InputGroup>
                    <InputGroupAddon addonType="prepend" >
                        <InputGroupText>filter:</InputGroupText>
                    </InputGroupAddon>
                    <Input />
                </InputGroup>
            </Row>
            <CardContainer
                items={items}
                cardRef={cardRef}
                onClickHandler={onClickHandler}
            />
        </>
    )
}

export default SectionContainer

卡片容器

import React from 'react'
import { Container, Row } from 'reactstrap'
import CardComponent from './CardComponent'

const CardContainer = ({ items, cardRef, onClickHandler }) => 
{
    const getItems = (items) =>
    {
        return items.map(item =>
        {
            return <Row key={item.id}>
                <CardComponent
                    item={item}
                    cardRef={cardRef}
                    onClickHandler={onClickHandler}
                />
            </Row>
        })
    }
    return (
        <Container fluid className="node-container">
            {getItems(items)}
        </Container>
    )
}

export default CardContainer

卡片组件

import React, { useState } from 'react';

import { Card, CardHeader, CardTitle, CardBody, CardText } from 'reactstrap';

const CardComponent = React.forwardRef(({ item, cardRef, onClickHandler }, ref) => 
{
    return (
        <Card ref={cardRef} >
            <CardHeader onClick={onClickHandler} >
                <CardTitle>{item.name}</CardTitle>
            </CardHeader>
            <CardBody>
                <CardText>{item.description}</CardText>
            </CardBody>
        </Card>)
})

export default CardComponent

这样显示类似上图的卡片没问题。我只是无法让 ref 正常工作。

即使我用 React.ForwardRef():

包装了 CardComponent,我仍然收到以下错误

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Check the render method of ForwardRef. in Card (at CardComponent.js:8) ...

编辑

我通过将卡片包装在 div 中修复了 forwardRef() 错误。即使 bootstrap 卡片呈现为 div,您也无法引用它。

只有一个位置传递给了 ContainerComponent。仍在努力。

新建卡片组件

import React, { useState } from 'react';

import { Card, CardHeader, CardTitle, CardBody, CardText } from 'reactstrap';

const CardComponent = ({ item, cardRef, onClickHandler }) =>
    (
        <div ref={cardRef}>
            <Card >
                <CardHeader onClick={onClickHandler} >
                    <CardTitle>{item.name}</CardTitle>
                </CardHeader>
                <CardBody>
                    <CardText>{item.description}</CardText>
                </CardBody>
            </Card>
        </div>
    )


export default CardComponent

使用 React Hooks,您基本上可以在功能组件中执行您在 class 组件中执行的任何操作。

我想你可以使用 getBoundingClientRect() 获得位置。

import React, { useRef } from 'react';
//import Card from '.../...';

const App = () =>
{
    const cardRef = useRef();

    const onClickHandler = () =>
    {
        const position = cardRef.current.getBoundingClientRect();
        //This returns an object with left, top, right, bottom, x, y, width, and height.
        console.log(position)
    }

    return (
        <div>
            <Card cardRef={cardRef} onClickHandler={onClickHandler} />
        </div>
    );

}

export default App

const Card = ({ cardRef, onClickHandler }) => (
    <div ref={cardRef} onClick={onClickHandler}>
        <button > Click Me </button>
    </div>
)

我要在这里回答我自己的问题。我欠社区的,因为我的问题很模糊。非常感谢@dlya 的回答让我走上了正义的道路:)

  • 是否可以仅使用功能组件来做到这一点?

是的。我能够在没有优雅组件的情况下端到端地完成任务。

  • 哪些组件需要包裹在 React.forwardRef() 中?

None。我最终根本不必使用 React.forwardRef() 。事实上......我也没有使用 useRef() 或 useEffect() 。我刚刚将一个函数传递给 child 并将该函数添加到 ref 中,如下所示:

<Card innerRef={updateCardLocations} id={`${type}-${item.id}`}>

请注意,我必须使用 innerRef 而不是 ref,因为我确实需要获取 reactstrap ref 而不是必须将 Card 包装在 div.

然后我就在 parent 中收集我所有的卡片位置,如下所示:

    let connections = {}

    const updateCardLocations = (card) =>
    {
        if (card)
        {
            const id = card.id.substr(card.id.indexOf("-") + 1)
            const pos = card.getBoundingClientRect()

            connections[id] = {
                ...connections[id],
                connectionPoint: [pos.right, pos.top + (pos.height / 2)]
            }

            console.log(connections)
        }
    }

  • 我读到我可以将一个函数从 Container 传递到 children ref 属性和 children 可以在单击时调用该函数。这是真的吗?那会是什么样子?

我刚刚在上一个项目符号中展示了它的样子。 parent 有一个名为 updateCardLocations 的函数,它作为 prop 传递给 child。将 updateCardLocations 函数 prop 传递给 Child 中的 ref。就我而言 innerRef.

  • 奖励:我已经通读了 多次 React Refs 和 DOM 文档,它只是 不会点击我。谁能根据我的情况解释一下?

我在文档中遗漏的是,您只是在 parent 中创建一个占位符,并将其作为道具传递给 child 组件。然后,当呈现 child 组件时,它会使用 dom 信息更新该占位符。在我获得成功的实践经验之前,我不会完全掌握事物......但是在我的辩护中,整个 forwardRef 的事情真的让我感到困惑,因为我不知道我正在向哪个方向转发裁判,当时我仍然以为我在 parent 中也使用 useRef() 创建了一个 ref。希望这能帮助像我一样容易混淆的人:)

这是 working sample on CodeSandbox。我还包含了一个添加卡片的 onClickHandler,您可以在控制台中看到更新的位置。