Search code examples
reactjsreduxmiddleware

Can't update react state, even component is not unmounted


Description

I have component which shows data that get from server and display it on the table using the state, tableData and it must be set when Redux action is dispatched.

I've use action listener library which uses Redux middleware which consisting of 63 lines of code. redux-listeners-qkreltms.

For example when I register a function on analysisListIsReady({}).type which is ANALYSISLIST_IS_READY then when the action is dispatched, the function is called.

Issue

The issue is that react throws sometimes the error: Can't update react state... for setTableData so response data is ignored to be set. I want to figure it out when it happens.

I've assumed that it's because of unmounting of component, so I printed some logs, but none of logs are printed and also ComponentA is not disappeared.

It's not throing any error when I delete getAnalysisJsonPathApi and getResource, so I tried to reporuduce it, but failed... link

It's not throing any error when I delete listenMiddleware.addListener see: #2

#1

// ComponentA
const [tableData, setTableData] = useState([])
useEffect(() => {
  return () => {
    console.log("unmounted1")
}}, [])  

useEffect(() => {
    listenMiddleware.addListener(analysisListIsReady({}).type, (_) => {
      try {
        getAnalysisJsonPathApi().then((res) => {
          //...
          getResource(volumeUrl)
            .then((data: any) => {
              // ...
              setTableData(data)
            })
        })
      } catch (error) {
        warn(error.message)
      }
    })

    return () => {
      console.log("unmounted2")
    }
  }, [])
export const getAnalysisJsonPathApi = () => {
  return api
    .post('/segment/volume')
    .then(({ data }) => data)

export const getResource = async (src: string, isImage?: boolean): Promise<ArrayBuffer> =>
  api
    .get(src)
    .then(({ data }) => data)

#2

// ComponentA
const [tableData, setTableData] = useState([])
useEffect(() => {
  return () => {
    console.log("unmounted1")
}}, [])  

useEffect(() => {
    if (steps.step2a) {
      try {
        getAnalysisJsonPathApi().then((res) => {
          //...
          getResource(volumeUrl)
            .then((data: any) => {
              // ...
              setTableData(data)
            })
        })
      } catch (error) {
        warn(error.message)
      }
    }

    return () => {
      console.log("unmounted2")
    }
  }, [steps.step2a])

Solution

  • I've found out it's because of redux-listeners-qkreltms, Redux middleware.

    It keeps function when component is mounted into listener, but never changes its functions even component is unmounted.

      middleware.addListener = (type, listener) => {
        for (let i = 0; i < listeners.length; i += 1) {
          if (listeners[i].type === type) {
            return;
          }
        }
        listeners.push(createListener(type, listener));
      };