Sunday, 21 February 2021

How to show/hide component in parent component via a button in a child component when using a map of buttons?

A bit of a mouthful question...

I've looked over dozen of similar questions, but they either have to do with components in the same file, or with singular buttons.

Desired Effect:
I'm trying to display repository commits once a user clicks on a card of a specific repo. The commits are hidden until they press a button. If the user clicks the button when the commits are showing, it should hide the commits. Continuous clicking of a repo card should show/hide/show/hide/etc. the appropriate commit cards.

function Home() {
    const title = "Projects"
    const message = "Projects fetched from Github using their GQL API."
    const [clicked, setClicked] = useState(false)
    const [repoInfo, setRepoInfo] = useState({ name: "Website", owner: "FernandoH-G" })

    return (
        <Container>
            <Jumbo title={title} message={message} />
            <CardDeck>
                <RepoCards clicked={clicked} setClicked={setClicked} setRepoInfo={setRepoInfo} />
            </CardDeck>
            <br/>
            <CardDeck>
                {clicked && <CommitCards repoInfo={repoInfo} />}
            </CardDeck>
        </Container>
    );
}
function RepoCards(props) {
    const { loading, error, data } = useQuery(GET_PINNED_REPOS);
    const [radioValue, setRadioValue] = useState("");

    if (loading) return (
        <Loading
            message="Fetching pinned repositories..."
            color="secondary" />
    )
    if (error) return (
        <Loading
            message="Error fetching pinned repositories."
            color="danger" />
    )
    const pinEdges = data.user.pinnedItems.edges

    return (
        pinEdges.map((pin, idx) => (
            <Card
                key={pin.node.name}
                className="text-center"
                border="light">
                <Card.Body>
                    <Card.Header as="h5"> {pin.node.name}</Card.Header>
                    <Card.Link href={pin.node.url}>
                        <Card.Img variant="top" src={chooseIMG(pin.node.name)} />
                    </Card.Link>
                    <Card.Text> {pin.node.description}</Card.Text>
                </Card.Body>
                <ButtonGroup toggle>
                    <ToggleButton
                        key={pin.node.name}
                        type="radio"
                        variant="outline-secondary"
                        name="Button Radio"
                        value={pin.node.name}
                        checked={radioValue === pin.node.name}
                        onChange={(e) => setRadioValue(e.currentTarget.value)}
                        onClick={() => {
                            console.log(`${idx} card clicked. click value: ${props.clicked}`)
                            props.setRepoInfo(
                                {
                                    name: pin.node.name,
                                    owner: pin.node.owner.login
                                })
                            props.setClicked(!clicked) // This should work,right?

                            // if (radioValue === pin.node.name) {
                            //  // props.setClicked(prevVal => !prevVal)
                            //  props.setClicked(false)
                            // } else {props.setClicked(true)}
                        }}>
                        Last Update:{' '}
                        {parseDate(pin.node.pushedAt)}
                    </ToggleButton>
                </ButtonGroup>
            </Card>
        ))
    )
}

I'm including the CommitCards component code even though I don't think it's necessary.

const CommitCards = (props) => {
    const name = props.repoInfo.name
    const owner = props.repoInfo.owner
    const { loading, error, data } = useQuery(GET_REPO_COMMITS, {
        variables: { name, owner },
    });
    if (loading) return (
        <Loading message={`Fetching ${name} commits...`} color="secondary" />
    );
    if (error) return (
        <Loading message={`Error fetching ${name} commits.`} color="danger" />
    );

    const commits = data.repository.defaultBranchRef.target.history.edges
    return commits.map(com => (
        <Card
            key={com.node.url}
            bg={"dark"}
            style=
            border="info">
            <Card.Body>
                <Card.Link href={com.node.url}>
                    Commit Date:{'\n'}
                    {parseDate(com.node.committedDate)}
                </Card.Link>
                <Card.Text>
                    {parseText(com.node.message)}
                </Card.Text>
            </Card.Body>
        </Card>
    ))
}

After looking at the console, and recording the state of clicked, it may be a rendering issue. I say this because it seems to be rendering everything twice, thus messing with the value of clicked.

I don't know. I've been on this for a few days now. Below is a screenshot of the consoles output after I click each button. Commits fail to even show; not close to having the effect I desire.

console output

Huge development

Following the suggestion of @deckele, I was working on a code sandbox when I noticed that my initial code posted...actually works, sorta. I can definitely optimize the posted code. I wasn't able to get react-bootstrap to work in code sandbox, but that turned out for the better.

It turns out that a <ToggleButton> reads clicking the label, the text portion, as a click on the console, twice. However, you have to hit the very small actual input area. You can check out the label registering as a click in the code sandbox.
input_covered
Unfortunately, the input area is covered by the label, which is essentially the whole button.

Since this question took a turn, I will be closing up this one, and probably opening up a new react-bootstrap one.



from How to show/hide component in parent component via a button in a child component when using a map of buttons?

No comments:

Post a Comment