chainRef function

CallbackRef? chainRef(
  1. ReactElement element,
  2. CallbackRef? newCallbackRef
)

Returns a function that chains the callback or createRef-based ref of the provided element with newCallbackRef.

If there is no existing ref, newCallbackRef is used.

Throws an ArgumentError if ReactElement.ref is a String ref.

TODO: This method makes assumptions about how react-dart does callback refs for dart components, so this method should be moved there (UIP-1118).

Implementation

CallbackRef? chainRef(ReactElement element, CallbackRef? newCallbackRef) {
  final existingRef = element.ref as Object?;

  // If there's no existing ref, just return the new one.
  if (existingRef == null) return newCallbackRef;

  if (existingRef is String) {
    throw ArgumentError.value(existingRef, 'element.ref',
        'The existing ref is a String ref and cannot be chained');
  }

  if (existingRef is Function) { // Callback refs
    void chainedRef(ref) {
      // For Dart components, the existing ref is a function passed to the JS that wraps the Dart
      // callback ref and converts the JS instance to the Dart component.
      // So, we need to undo the wrapping around this chained ref and pass in the JS instance.
      existingRef(ref is react.Component ? ref.jsThis : ref); // ignore: deprecated_member_use

      if (newCallbackRef != null) newCallbackRef(ref);
    }

    return chainedRef;
  } else { // Assume it's a ref object created by createRef
    assert(existingRef is! Ref,
        'should be a JsRef; the factory that created `element` should never let a Dart ref get here');
    void chainedRef(ref) {
      // For Dart components, the existing ref is a Dart Ref object that converts
      // the JS instance on the JS ref to the Dart component.
      // So, we need to undo the wrapping around this chained ref and pass in the JS instance.
      //
      // Update `.current` manually on the ref object.
      // As per https://github.com/facebook/react/issues/13029 it should be fine to set current this way.
      // Use setProperty so we don't have to expose a `JsRef.current` setter, which could be misused.
      setProperty(existingRef, 'current', ref is react.Component ? ref.jsThis : ref); // ignore: deprecated_member_use

      if (newCallbackRef != null) newCallbackRef(ref);
    }

    return chainedRef;
  }
}