Reputation: 153
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
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