Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref doesn't work with dynamically imported components #4957

Closed
juancampa opened this issue Aug 15, 2018 · 20 comments
Closed

ref doesn't work with dynamically imported components #4957

juancampa opened this issue Aug 15, 2018 · 20 comments

Comments

@juancampa
Copy link
Contributor

Bug report

Describe the bug

When a component is wrapped in next/dynamic the passed ref should be a ref to the imported component, not an instance of NoSsr or LoadableComponent

To Reproduce

  1. Import a component using next/dynamic
  2. Use said component in render passing a ref (use a function to received the ref)

Result: the function is called with either a NoSSR component or a LoadableComponent instead of an instance of the component imported in step 1

Expected behavior

The function gets called with an instance of the dynamically loaded component

System information

  • Version of Next.js: 6.1.1-canary.4

Additional context

Didn't try with createRef but it shouldn't make a difference

@juancampa
Copy link
Contributor Author

This was reported in #2842 (closed) but since there's a new API for forwarding refs I believe it should be reconsidered

@timneutkens
Copy link
Member

Please create a minimal reproducible example in a github repo so it's easier to reproduce.

@juancampa
Copy link
Contributor Author

juancampa commented Aug 16, 2018

Sure, here it is:

https://github.com/juancampa/next-bug/blob/master/pages/index.js

@timneutkens
Copy link
Member

Expecting this to be one of the cases for the new createRef API

@HaNdTriX
Copy link
Contributor

HaNdTriX commented Aug 17, 2018

Solution

@juancampa You can use the forwardRef api to implement it yourself.

pages/index.js

import React from 'react'
import dynamic from 'next/dynamic';

// Dynamically imported component
const Component = dynamic(import('../components/Component'));
const ForwardedRefComponent = React.forwardRef((props, ref) => (
  <Component {...props} forwardedRef={ref} />
))

export default class Index extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef();
  }

  componentDidMount() {
    console.log('this.myRef', this.myRef)
  }

  render() {
    return <ForwardedRefComponent ref={this.myRef}/>;
  }
}

components/Component.js

export default ({ forwardedRef }) => (
  <div ref={forwardedRef}>
    Hi from Component
  </div>
)

Please note: You can also omit the forwardRef api and pass the ref as forwardedRef in directly.

Example

Issues adding this functionality to next.js

I don't think it makes sense to implement it in next now since:

  • react-loadable needs to implement it first
  • React <16.3 needs to be deprecated first
  • won't work with other rendering engines (e.g. preact)

@timneutkens
Copy link
Member

timneutkens commented Aug 17, 2018

Thanks @HaNdTriX!

@mohsinulhaq
Copy link

@HaNdTriX what if we want to set ref on the Component component itself?

@timneutkens
Copy link
Member

Going to close this as the issue has been answered and @HaNdTriX's reasoning is correct.

@muhaimincs
Copy link
Contributor

is this going to be official workaround?

@phattranky
Copy link

@HaNdTriX what if we want to set ref on the Component component itself?

Then, you can simply user other Component wrap all component using ref. Then use dynamic on WrapperComponent. So the child component will use like usual

@xfause
Copy link

xfause commented Mar 16, 2020

Solution

@juancampa You can use the forwardRef api to implement it yourself.

pages/index.js

import React from 'react'
import dynamic from 'next/dynamic';

// Dynamically imported component
const Component = dynamic(import('../components/Component'));
const ForwardedRefComponent = React.forwardRef((props, ref) => (
  <Component {...props} forwardedRef={ref} />
))

export default class Index extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef();
  }

  componentDidMount() {
    console.log('this.myRef', this.myRef)
  }

  render() {
    return <ForwardedRefComponent ref={this.myRef}/>;
  }
}

components/Component.js

export default ({ forwardedRef }) => (
  <div ref={forwardedRef}>
    Hi from Component
  </div>
)

Please note: You can also omit the forwardRef api and pass the ref as forwardedRef in directly.

Example

Issues adding this functionality to next.js

I don't think it makes sense to implement it in next now since:

  • react-loadable needs to implement it first
  • React <16.3 needs to be deprecated first
  • won't work with other rendering engines (e.g. preact)

how can i do with the custom Component not wroten by myself ?
I cant set

export default ({ forwardedRef }) => (
  <div **ref={forwardedRef}**>
     Hi from Component
  </div>
)

@matiastucci
Copy link
Contributor

I'm trying to use forwardRef on an external component but ref.current is not the actual ref. Not sure if I'm missing something. Can anyone take a look, please?

Here it's my code: https://codesandbox.io/s/objective-benz-qh4ec?file=/pages/index.js:63-73

@alvelig
Copy link

alvelig commented Dec 15, 2020

If you can't modify an imported component, then you'll have to hack into the DOM.

My workaround (or maybe it's just a forgotten canonical way?) is the following:

const Component = () => {
  const ref = useRef();
  const id = useState(shortid());
  const DynamicComponent = eval('div') // Put whatever variable that contains your dynamic component
  useEffect(() => {
    ref.current = document.getElementById(id);
  }, [DynamicComponent]);
  return <DynamicComponent id={id} />
}

Be aware of that if you change your dynamic component due to some conditions, you need to add those conditions to useEffect so the ref is updated with your component.

@matiastucci
Copy link
Contributor

@alvelig thanks for the response!

Sadly, I'm not sure if I follow this part:

eval('div') // Put whatever variable that contains your dynamic component

Can you explain it more, please?

Thanks 🙏

@fcFn
Copy link

fcFn commented Feb 22, 2021

@matiastucci You have to wrap your component in a custom component and pass the ref through it.

See your modified example: https://codesandbox.io/s/relaxed-pine-b8f5z.

Note that you can use the custom prop way without using forwardRef (example: https://codesandbox.io/s/busy-jennings-cxll6).

@giacomoalonzi
Copy link

@matiastucci You have to wrap your component in a custom component and pass the ref through it.

See your modified example: https://codesandbox.io/s/relaxed-pine-b8f5z.

Note that you can use the custom prop way without using forwardRef (example: https://codesandbox.io/s/busy-jennings-cxll6).

In your example the ref is null, is this the proper result?

@fcFn
Copy link

fcFn commented Apr 19, 2021

@giacomoalonzi If you check the console pane (not the terminal pane), you'll see that the ref is not null after the editor renders.

@igormartimiano
Copy link

@fcFn Much appreciated, thanks!

@ical10
Copy link

ical10 commented May 27, 2021

Solution

@juancampa You can use the forwardRef api to implement it yourself.

pages/index.js

import React from 'react'
import dynamic from 'next/dynamic';

// Dynamically imported component
const Component = dynamic(import('../components/Component'));
const ForwardedRefComponent = React.forwardRef((props, ref) => (
  <Component {...props} forwardedRef={ref} />
))

export default class Index extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef();
  }

  componentDidMount() {
    console.log('this.myRef', this.myRef)
  }

  render() {
    return <ForwardedRefComponent ref={this.myRef}/>;
  }
}

components/Component.js

export default ({ forwardedRef }) => (
  <div ref={forwardedRef}>
    Hi from Component
  </div>
)

Please note: You can also omit the forwardRef api and pass the ref as forwardedRef in directly.

Example

Issues adding this functionality to next.js

I don't think it makes sense to implement it in next now since:

  • react-loadable needs to implement it first
  • React <16.3 needs to be deprecated first
  • won't work with other rendering engines (e.g. preact)

Thanks a bunch! Works with my next.js + typescript project, you just need to add simple snippets below in Component.ts:

interface ComponentProps {
  forwardedRef: React.ForwardedRef<any>;
}

const Component: React.FC<ComponentProps> = ({ forwardedRef }) => {
...
}

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests