Pim_vh
Pim_vh

Reputation: 153

React ref current is null outside component where it is set

I am running into a problem with React ref.

This morning I was wondering how I could access DOM elements from sub-components within one main component, and on this site I found a solution in React refs. I created a ref in my main component and binded it to a DOM element in my sub component.

However, when I try to access the value from my main component it throws an error saying that current is null. Code snippets:

Main component (ProfileSetup):

export default function ProfileSetup() {
    const userName = React.createRef();
    const [activeStep, setActiveStep] = React.useState(0);

    function getStepContent(step) {
        switch (step) {
            case 0:
                return <PersonalDetails userName={userName} />;
            case 1:
                return <PlatformDetails userName={userName}/>;
            case 2:
                return <Wishlist userName={userName}/>;
            default:
                throw new Error('Unknown step');
        }
    }

    const handleNext = () => {
        if(activeStep === steps.length - 1){
            register();
        }
        setActiveStep(activeStep + 1);
    };

    const register = () => {
        console.log(userName);
    }
}

Sub-component (PersonalDetails):

export default function PersonalDetails(props) {
    function test(){
        console.log(props.userName.current.value);
    }

    return (
        <React.Fragment>
            <Typography variant="h6" gutterBottom>
                Personal details
            </Typography>
            <Grid container spacing={3}>
                <Grid item xs={12}>
                    <TextField
                        required
                        id="emailAddress"
                        name="emailAddress"
                        label="Email address"
                        fullWidth
                        autoComplete="emailAddress"
                    />
                </Grid>
                <Grid item xs={12}>
                    <TextField
                        required
                        id="userName"
                        name="userName"
                        label="Username"
                        fullWidth
                        autoComplete="userName"
                        inputRef={props.userName}
                        onChange={test}
                    />
                </Grid>
                <Grid item xs={12} sm={6}>
                    <TextField
                        required
                        id="password"
                        name="password"
                        label="Password"
                        type="password"
                        fullWidth
                        autoComplete="password"
                    />
                </Grid>
                <Grid item xs={12} sm={6}>
                    <TextField
                        required
                        id="confirmPassword"
                        name="confirmPassword"
                        label="Confirm password"
                        type="password"
                        fullWidth
                        autoComplete="confirmPassword"
                    />
                </Grid>
            </Grid>
        </React.Fragment>
    );
}

As you can see, I binded the ref to my userName textfield. The console log inside the onchange here does return the right value, but the one in the register function of the main component throws the error. Any ideas what I am doing wrong?

Upvotes: 0

Views: 2428

Answers (1)

Faktor 10
Faktor 10

Reputation: 1888

You are trying to pass a ref down through props. In strict React speak you are trying to Forward a ref. This feature is an opt in feature. I.e. it does not work straight out of the box. In order to allow this you need to wrap your child component in a render function called React.forwardRef(). So what you code should probably look like is this:

export default function ProfileSetup() {
    const userName = React.createRef();
    const [activeStep, setActiveStep] = React.useState(0);

    function getStepContent(step) {
        switch (step) {
            case 0:
                return React.forwardRef((props, userName)) => (
                    <PersonalDetails {...props} userName={userName} />;
                )
            case 1:
                return React.forwardRef((props, userName)) => (
                  <PlatformDetails {...props} userName={userName}/>;
                )
            case 2:
                return React.forwardRef((props, userName)) => (   
                  <Wishlist {...props} userName={userName}/>;
                )
            default:
                throw new Error('Unknown step');
        }
    }

    const handleNext = () => {
        if(activeStep === steps.length - 1){
            register();
        }
        setActiveStep(activeStep + 1);
    };

    const register = () => {
        console.log(userName);
    }
}

The official React docs on this do a better job explaining than me, Forwarding Refs

Upvotes: 1

Related Questions