skip to Main Content

I have the following code and it is causing my Dashboard component to render a few times. How can I reduce it to only once?

Dashboard

const Dashboard: React.FC = () => {
    const { account } = useActiveWeb3React();
    const { 
        status: status1, 
        loading: loading1, 
        error: error1, 
        response: response1, 
        payload: payload1 
    } = useAllLPTokens()
... ...

Custom Hook

const useAllLPTokens = (): GraphQLResponse<LPTokens> => {
    const [status, setStatus] = useState<number>(0);
    const [loading, setLoading] = useState<boolean>(false);
    const [error, setError] = useState<any>();
    const [response, setResponse] = useState<any>();
    const [payload, setPayload] = useState<LPTokens | undefined>();

    const getLPTokenData = async () => {
        setLoading(true);
        try {
            const res = await axios.post(subgraphEndpoint,
                {
                    headers: { "Content-Type": "application/json" },
                    query: graphQuery
                }
            );
            setStatus(res.status);
            setResponse(res)
            setPayload(res.data)
        } catch (error) {
            setError(error)
        }
        setLoading(false);
    }

    useEffect(() => {
        getLPTokenData();
    }, [])

    return { status, loading, error, response, payload }
}

I’m guessing its because of all the useState in the custom hook, how can I change my code?

2

Answers


  1. I guess you can have a single hook that stores the state like this:

      const [state, setState] = useState({loading: false, res: null, error: null});
    
      const getLPTokenData = async () => {
            setState(prev => ({...prev, loading: true}));
            try {
                const res = await axios.post(subgraphEndpoint,
                    {
                        headers: { "Content-Type": "application/json" },
                        query: graphQuery
                    }
                );
                setState({loading: false, res, error: null});
            } catch (error) {
                setState({loading: false, res: null, error});
            }
        }
    
        useEffect(() => {
            getLPTokenData();
        }, [])
    
      return { 
          status: state.res?.status || 0,
          loading: state.loading,
          error: state.error,
          response: state.res,
          payload: state.res?.data
      }
    

    Doing this you are sure that UI batch updates but my advice is not to worry too much about renders and rerenders in React, the library itself is really smart to understand what actually changed between renders and update the DOM.
    The code you posted is perfectly fine, I would not change it.

    Login or Signup to reply.
  2. If you use React.strictMode, useEffect will run two times.
    Suppose you are using un-strict Mode now.
    If you add

    console.log('render', status, res);
    

    in your Dashboard. you will see

    // init render
    App.js:29 render 0 undefined
    
    // after loading status set re-render
    App.js:29 render 0 undefined
    
    // after data is resolve re-render
    App.js:29 render 1243 {successResponse: true, code: '200', data: {…}, requestId: 'eD610Be4-AB3b-f9fe-DcC6-Fa2F0F612DfE', id: ''}
    
    

    Here you can delete loading status to reduce the second render. set loading component base on response

    const ifLoading = !Boolean(res)
    

    By the way, duplicate states(status、payload etc.) is useless, always make unexpected bug.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search