I'm trying to do something like this:
When I click on a card on the left and then ctrl+click on a card on the right.. a line will be drawn. Ctrl+click on the card on the right again and the line will be removed. I'm going to draw the line using an svg path but I have to get the positions of the cards in the Container Component to do so.
I'm trying to do this with React Function Components using ref and I'm not quite getting it. I understand that function components don't have an "instance" and therefore ref doesn't work quite like it does in classes.
Please answer the following:
- Is it possible to do this with just function components?
- Which components require to be wrapped in React.forwardRef()?
- I read that I can just pass a function from the Container to the children in the ref attribute and the children can call that function when clicked. Is that true and what would that look like?
- bonus: I've read through the React Refs and the DOM documentation several times and it just won't click for me. Can anyone explain it in my context?
Any help is appreciated and thanks in advance!
EDIT
This is what I have so far:
ContainerComponent
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
Card Container
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
Card Component
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
This displays the cards similar to the drawing above no problem. I just can't get the ref thing to work.
I'm getting the following error for CardComponent even tho I have wrapped it with React.ForwardRef():
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) ...
Edit
I fixed the forwardRef() error by wrapping my Card in a div. Even tho the bootstrap Card renders as a div, you can't ref it.
Only one position is passing up to the ContainerComponent tho. Still working on that.
New CardComponent
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
