Kishan Jaiswal
Kishan Jaiswal

Reputation: 664

how can i get child state value to parent using useRef in reactjs + typescript (hooks)

I am new to Typescript. I need to get the child state values to the parent using ref on a button click to update the reducer values.

I have tried passing a ref to the child but I am getting errors similar to this:

Type '{ value: string; onChange: Dispatch<SetStateAction>; ref: MutableRefObject<HTMLInputElement | undefined>; }' is not assignable to type 'IntrinsicAttributes & Props & { children?: ReactNode; }'.

Property 'ref' does not exist on type 'IntrinsicAttributes & Props & { children?: ReactNode; }'.ts(2322)

parent component

import React, from "react";
import styled from "styled-components";
import {
    Page,
    Welcome,
    ErrorBoundary
} from "components";
const ParentDiv = styled.div`
            margin: 0 410px 30px 15px;
            `;

export const CreateEvent = (props: any) => {

    return (
        <Page title='Dashboard'>
            <ErrorBoundary>
                {(() => {
                    switch (activeEventStage?.step) {
                        case 1:
                            return (
                                <ErrorBoundary>
                                    <Welcome />
                                </ErrorBoundary>
                            );
                        default:
                            return null;
                    }
                })()}
            </ErrorBoundary>
        </Page>
    );
};
export default withRouter(CreateEvent);

child component

import React, { useState } from "react";
import { Row, Col } from "react-bootstrap";

export const Welcome = () => {
    const { t } = useTranslation();
    const [state, setState] = useState({
        welBannerTagline: "",
        welHeroTitle: "",
    });

    return (
        <CreateEventFormContainer
            title={t("event.create.title")}
            subTitile={t("welcome.subTitle")}
        >
            <>
                <Row>
                    <Col lg='6'>
                        <DropZoneInputField
                            titleName={t("welcome.bgImage.title")}
                            onSelectedFiles={onDropFiles}
                            imageType='bgImage'
                            value={state.welBgFilename}
                        />
                    </Col>
                    <Col lg='6'>
                        <DropZoneInputField
                            titleName={t("welcome.banner.title")}
                            onSelectedFiles={onDropFiles}
                            imageType='bannerImage'
                            value={state.welBannerFilname}
                        />
                    </Col>
                </Row>
            </>
        </CreateEventFormContainer>
    );
};
export default Welcome;

Upvotes: 2

Views: 1695

Answers (2)

Jorge Piquer
Jorge Piquer

Reputation: 156

A much simpler way is to use the useRef hook built into React with the reserved ref prop. It only works on mutable objects so you don't have to worry about typing it yourself.

import { useRef } from "react";

export const CreateEvent = (props: any) => {

    const welcomeRef = useRef();

    return (
        <Page title='Dashboard'>
            <ErrorBoundary>
                {(() => {
                    switch (activeEventStage?.step) {
                        case 1:
                            return (
                                <ErrorBoundary>
                                    <Welcome ref={welcomeRef} />
                                </ErrorBoundary>
                            );
                        default:
                            return null;
                    }
                })()}
            </ErrorBoundary>
        </Page>
    );
};
export default withRouter(CreateEvent);

Upvotes: 1

Linda Paiste
Linda Paiste

Reputation: 42188

You get that error because Welcome is not designed to accept a ref prop.

ref is a reserved prop name in React so in order to pass a prop ref to a child component, that child must use React.forwardRef to accept the ref prop.

You can pass the ref object directly with any other prop name, for example myRef. In that case you don't need to use forwardRef but you do need to make sure that the props of the child accept a prop myRef which is a ref.

The type for the ref itself depends on where it will be eventually attached to the DOM. Probably an HTMLButtonElement?

interface WelcomeProps {
    myRef: React.MutableRefObject<HTMLButtonElement | null>;
}

export const Welcome = ({myRef}: WelcomeProps) => {
...
export const CreateEvent = (props: RouteComponentProps) => {
    const refObject = React.useRef<HTMLButtonElement | null>(null);
    return (
...
       <Welcome myRef={refObject}/>
...

This allows you to interact with the DOM. But that doesn't seem like what you actually want to do.

I need to get the child state values to the parent using ref on a button click to update the reducer values.

Just pass a callback! Should the state even be stored in the child, or can you lift it up to the parent?

interface CallbackValue {
    // what properties does the callback want to receive?
    welBannerTagline: string;
    welHeroTitle: string;
}

interface WelcomeProps {
    myCallback: (value: CallbackValue) => void;
}

export const Welcome = ({myCallback}: WelcomeProps) => {
    const [state, setState] = useState({
        welBannerTagline: "",
        welHeroTitle: "",
    });

    const handleEvent = () => {
        myCallback(state);
    }
...
export const CreateEvent = (props: RouteComponentProps) => {
    return (
...
       <Welcome myCallback={(value) => {
           // do something here with the value
       }} />
...

Upvotes: 1

Related Questions