0

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?

Pim_vh
  • 143
  • 1
  • 14
  • See if you can get an answer from [this](https://stackoverflow.com/a/42555112/14259273) – mplusr Nov 24 '20 at 10:03
  • I guess a better approach is to handle the values by `state`, `onChange` inside you main component and pass them to your fields thro `props`. Here is a complete [sandbox](https://codesandbox.io/s/react-material-ui-step-form-ui788?file=/src/components/StepForm.js) example that might help. – awran5 Nov 24 '20 at 10:40
  • Why are you even using a ref? All you're doing is trying to associate an input value with a state. – Jared Smith Nov 24 '20 at 11:54
  • @JaredSmith I saw this as an answer to a similar question I had: how do I get values from dom elements from other components within my main component. – Pim_vh Nov 24 '20 at 12:39
  • @Pim_vh generally for things like textfields you will simply pass down the setter from useState and update the value on the element's change or input event. You don't usually use refs for that. – Jared Smith Nov 24 '20 at 14:32

1 Answers1

1

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

Faktor 10
  • 1,868
  • 2
  • 21
  • 29
  • 1
    Doing this I get the following error which I don't quite understand: ```Objects are not valid as a React child (found: object with keys {$$typeof, render}). If you meant to render a collection of children, use an array instead.``` – Pim_vh Nov 24 '20 at 10:32
  • oops it might also be expecting your original props, I updated above code. Another article might help, https://medium.com/@rossbulat/how-to-use-react-refs-4541a7501663 – Faktor 10 Nov 24 '20 at 11:45
  • I'm getting the same problem as said in the question, the current is still null – Pim_vh Nov 24 '20 at 12:36