How to test a react native component whose UI changes based on async call in useEffect hook

114 views Asked by At

I have a react native component OrdersScreen which I am not able to test because the component gets updated when setState : setRenderLoader(false) is called in React.useEffect() hook.

  const OrdersScreen = ({ navigation }) => {
  let [renderLoader, setRenderLoader] = React.useState(true);
  let [ordersInfo, setOrdersInfo] = React.useState([]);
  React.useEffect(() => {
    ordersController.getOrdersForAgent() // some API call
      .then((res) => {
        console.log("getOrdersForAgent res", res);
        setOrdersInfo(res);
        if (res)
          res.forEach(ord => dispatch(addOrder(ord)));
        setRenderLoader(false);
      })
      .catch((err) => {
        console.log("getOrdersForAgent err", err);
      });
  }, []);
return (
    <View
      style={ordersScreenStyle.container}>
      {renderLoader
        ?
        (<Loader />)
        :
        (<SectionList
          ...
        />)}
    </View>

Below is the test code I have:

import renderer from 'react-test-renderer';
import {act} from 'react-test-renderer'

describe('<OrdersScreen/>', () => {
    test('Verify order item', async () => {
        const ORDER_ITEMS = DEFAULT_VALUE;
        ordersController.getOrdersForAgent = jest.fn(() => Promise.resolve(ORDER_ITEMS));
        const setRenderLoader = jest.fn();

        let component;
        await act(async () => {
            //render with redux just wraps renderer.create(component) with a redux store
            component = await renderWithRedux(<OrdersScreen />);
        })
        const root = component.root;
        let tree = component.toJSON();
        console.log('tree', tree);
        if (store.getState().ordersReducer.length === 0) {
            expect(setRenderLoader).not.toHaveBeenCalled();
        }
        else {
            expect(setRenderLoader).not.toHaveBeenCalledWith(false);
        }



    })
})

I get the below error:

    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

      142 |         if (res)
      143 |           res.forEach(ord => dispatch(addOrder(ord)));
    > 144 |         setRenderLoader(false);

Could anyone please give pointers as to how this component is to be tested? I am quite new to this. Any help is appreciated.

2

There are 2 answers

0
Ahmed Sbai On

If dispatch(addOrder(ord)) is async then you want to handle those actions inside a for..of loop instead of forEach.

The forEach loop acts differently than the for loop, while the for loop await the iteration before moving further, the forEach loop executes all of the iterations simultaneously.

for (const ord of res){
 dispatch(addOrder(ord))
}
0
Sweety SK On
import "./App.css";

function App() {
    let [renderLoader, setRenderLoader] = React.useState(true);
  let [ordersInfo, setOrdersInfo] = React.useState([]);
useEffect(() => {
    ordersController?.getOrdersForAgent() // some API call
      .then((res) => {
        console.log("getOrdersForAgent res", res);
        setOrdersInfo(res);
        if (res)
          res?.forEach(ord => dispatch(addOrder(ord)));
        setRenderLoader(false);
      })
      .catch((err) => {
        console.log("getOrdersForAgent err", err);
      });
  }, []);
  return (
    <div className="App">
      <header className="App-header">
        <img src="/logo.svg" className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.jsx</code> and save to reload! :)
        </p>
        <span className="App-link">Hello from codedamn :)</span>
      </header>
    </div>
  );
}

export default App;

Try with this code here i am only getting ordersController not imported error