reactjs / rfcs

RFCs for changes to React

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Feature Request] Return ref rather than forwardRef

xialvjun opened this issue · comments

What does it like:

function MyCom(props: {name: string}) {
  const whateverRef = useRef(whatever);
  return [whateverRef, <div>{props.name}</div>];
}

class MyClassCom extends Component {
  ref = createRef(whatever);
  render() {
    return <div></div>
  }
}

Why I proposed it:

When we use forwardRef in typescript, we need the generic types like

forwardRef<RefType, PropsType>((props, ref) => {}).

But what if the RefType depends on PropsType? like:

const plugins = {
  A: class {},
  B: class {},
  C: class {},
}
type Plugins = typeof plugins;
type PluginName = keyof Plugins;

const Plugin = forwardRef(<P extends PluginName>(props: {name: P}, ref) => {
  const ins = useMemo(() => {
    const Plugin = plugins[props.name];
    return new Plugin();
  }, []);
  useImperativeHandle(ref, () => ins, [ins]);
  return null;
})

I can never know the ref type.

I tried:

const Plugin = (<P extends PluginName>() => {
  type PluginType = Plugins[P];
  type RefType = InstanceType<PluginType>;
  type PropsType = { name: P };
  return  forwardRef<RefType, PropsType>((props, ref) => {
    const ins = useMemo(() => {
      const Plugin = plugins[props.name];
      return new Plugin();
    }, []);
    useImperativeHandle(ref, () => ins, [ins]);
    return null;
  });
})();

But the type I got in ref of Plugin component is always A | B | C rather than a specific plugin type.

With this proposal, we can:

const Plugin = <P extends PluginName>(props: {name: P}) => {
  const ins = useMemo(() => {
    const Plugin = plugins[props.name];
    return new Plugin();
  }, []);
  return [ins, null] as [InstanceType<Plugins[P]>, React.ReactNode];
}

And if it's class component:

class Plugin<P extends PluginName> extends Component<{name: P}> {
  ref = createRef<InstanceType<Plugins[P]>>();
  constructor(props) {
    super(props);
    const Plugin = plugins[props.name];
    this.ref.current = new Plugin();
  }
  render() {
    return null;
  }
}

The essence of this proposal

Component is essential a function(what ever ClassComponent or FunctionComponent)

ref is a deferred value produced in that function(normally we use callback to get that value)

return is the natural way to send a value out.

So, why not return the ref?

commented

Hi, thanks for your suggestion. RFCs should be submitted as pull requests, not issues. I will close this issue but feel free to resubmit in the PR format.