EHM
EHM

Reputation: 959

Is it possible to create a promise that get fulfilled by a state change in react-native?

I have a group of react-native components that have similar interfaces. The important method in the interface is the run method. Users can execute this run method by pressing an action button for each of the components or by pressing an action button in the parent level, that will iterate through each of these similar components using their ref and call the run method. Here is a simplified version of the setup.

const Test1 = forwardRef((props, ref) => {
  const [importantState, setimportantState] = useState('initial')
  useImperativeHandle(ref, () => ({
    run: run,
  }))

  const run = async () => {
    // This function will open a modal
    //  where users will eventually change the important state
    openInteractionModal()

    // The idea of this promiss is to settle when the users
    // eventually change the importantState thorugh thier interaction
    // It does so by checking the state every 100 miliseconds
    // This however is a problem as a result of the setInterval function will
    // inclose the initial state instead of getting the new value each time
    return new Promise((resolve, reject) => {
      let interval = setInterval(() => {
        if (importantStat === 'complete') {
          resolve('success')
          clearInterval(interval)
        } else if (importantStat === 'failed') {
          reject('failed')
          clearInterval(interval)
        }
      }, 100)
    })
  }
  return (
    <>
      <Button title="run" onPress={run} />
    </>
  )
})

const parentComponent = () => {
  const test1Ref = userRef(null)
  const test2Ref = userRef(null)
  const test3Ref = userRef(null)

  const testRefs = [test1Ref, test2Ref, test3Ref]

  const autoRun = async () => {
    testRefs.forEach(ref => {
        await ref?.current.run()
    });
  }

  return (
    <>
      <Button title="Auto run" onPress={autoRun} />
      <Test1 ref={test1Ref} />,
      <Test2 ref={test2Ref} />,
      <Test3 ref={test3Ref} />,
    </>
  )
}

The problem I am having right now is the promise I am returning from these individual components never settles as a result of the closure I mentioned above in the comments. Is there a way to overcome this closure, or is there a better react-native pattern that I am using right now?

Upvotes: 3

Views: 1557

Answers (2)

Bergi
Bergi

Reputation: 664886

Don't go polling. Instead, pass the resolve function as an argument to openInteractionModal, and call it together with setImportantState from the modal dialog.


If that is for some reason not possible, you can use a ref to store the resolve function and run it from an effect when the state changes:

const onImportantStateChange = useRef();
useEffect(() => {
  if (!onImportantStateChange.current) return;
  if (importantState === 'complete') {
    onImportantStateChange.current('success');
    onImportantStateChange.current = undefined;
  } else if (importantState === 'failed') {
    onImportantStateChange.current(Promise.reject(new Error('failure')));
    onImportantStateChange.current = undefined;
  }
}, [importantState])
const run = async () => {
  return new Promise(resolve => {
    onImportantStateChange.current = resolve;

    // This function will open a modal
    //  where users will eventually change the important state
    openInteractionModal();
  });
}

Upvotes: 6

Asad raza
Asad raza

Reputation: 71

Create a ref and store the status in that instead of the state of the Test1 component.

Like that

const importantState = useRef(null);

Set value in the current of the ref

importantState.current="complete"

Later use it in the setInterval like

if(importantState.current === "complete")

Upvotes: 1

Related Questions