是否有一个函数可以返回一个承诺,当在 React Native 中应用对组件的任何挂起的更改时,该承诺就会得到解决?

问题描述 投票:0回答:2

我想显示一个自定义输入组件,然后在单击按钮时调用其方法:

const Parent = () => {
  const customInputRef = useRef(null);

  const [customInputVisible, setCustomInputVisible] = useState(false);

  async function onPress() {
    setCustomInputVisible(true);

    await resolvePendingChanged(); // customInput is not null and can be accessed

    customInputRef.current.customMethod();
  }

  return (
    <View>
      <Button onPress={onPress}>Press me!</Button>

      {customInputVisible && <CustomInput ref={customInputRef} />}
    </View>
  );
}

我看到人们使用自定义forceUpdate函数来触发组件更新,但这对我来说并没有真正的帮助。

Svelte 中,有一个 “tick”生命周期挂钩,它正是我所需要的。

它返回一个承诺,一旦任何挂起状态就解决 更改已应用到 DOM(或者立即应用,如果没有 待定状态更改)。

React 中是否有相当于 Svelte 的

tick
的工具,如果没有,我该如何在 React 中解决这个问题?

reactjs react-native react-hooks lifecycle
2个回答
2
投票

您可以创建一个自定义钩子,使用 callback ref 来设置实际的 ref,并解析一个 Promise:

const { forwardRef, useImperativeHandle, useRef, useState, useCallback, useMemo } = React;

const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  
  useImperativeHandle(ref, () => ({
    customMethod: () => {
      inputRef.current.focus();
    }
  }), []);
  
  return <input ref={inputRef} />;
});

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
    });
  }
}

const waitForComponent = () => {
  const componentRef = useRef(null);
 
  return useMemo(() => {
    let deferred = new Deferred();
    
    return {
      waitLoad(ref) {
        componentRef.current = ref;
        
        if (ref) deferred.resolve();
        else deferred = new Deferred(); // create new Promise when ref is null
      },
      isLoaded: () => deferred.promise,
      componentRef
    };
  }, []);
}

const Parent = () => {
  const { waitLoad, componentRef, isLoaded } = waitForComponent();
  const [customInputVisible, setCustomInputVisible] = useState(false);

  function onPress() {
    setCustomInputVisible(visible => !visible);
     
    // use async here - SO snippet doesn't support async await
    isLoaded().then(() => componentRef.current.customMethod());
  }

  return (
    <div>
      <button onClick={onPress}>Press me!</button>

      {customInputVisible && <CustomInput ref={waitLoad} />}
    </div>
  );
};

ReactDOM.render(
  <Parent />,
  root
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>


0
投票

一点点进步。

const { forwardRef, useImperativeHandle, useRef, useState, useCallback, useMemo } = React;

  const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  
  useImperativeHandle(ref, () => ({
    customMethod: () => {
      inputRef.current.focus();
    }
  }), []);
  
  return <input ref={inputRef} />;
});

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
    });
  }
}

const useWaitForComponent = () => {
   
  return useMemo(() => {
      let deferred = new Deferred();
      const waitLoad = (ref)=> {
        
        if (ref) deferred.resolve(ref);
        else deferred = new Deferred(); // create new Promise when ref is null
      }
      const isLoaded = () => deferred.promise
    return [waitLoad, isLoaded];
  }, []);
}

const Parent = () => {
  const [ refButtonWait, isButtonLoaded ] = useWaitForComponent();
  const [customInputVisible, setCustomInputVisible] = useState(false);

  function onPress() {
    setCustomInputVisible(visible => !visible);
     
    // use async here - SO snippet doesn't support async await
    isButtonLoaded().then((ref) => ref.customMethod());
  }

  return (
    <div>
      <button onClick={onPress}>Press me!</button>

      {customInputVisible && <CustomInput ref={refButtonWait} />}
    </div>
  );
};

ReactDOM.render(
  <Parent />,
  root
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

© www.soinside.com 2019 - 2024. All rights reserved.