SoFunction
Updated on 2025-04-07

Use react-activation to implement keepAlive to support returning argument transmission

introduce

This project is a backend management system of a mall, built with umi2.0, and state management uses dva, so you want to achieve a similar effect to vue keep-alive.

Specifically manifested as:

Jump the details page of A from list page A, and list page A is cached

  • No operation is done on the details page, jump back to list page A, list page A is not refreshed, list page A is not changed
  • The details page was edited, jumped back to list page A, refreshed list page A, and the page number of list page A remains unchanged
  • The details page has been created in a new operation, jump back to list page A, list page A is refreshed, and the page number of list page A becomes 1

Jump to list page B from list page A, list page A does not cache

In summary, a page is cached only when it jumps to the specified page, and when returning to the cached page, it can control whether to refresh.

Code

1. Install react-activation

"react-activation": "^0.10.2",

2. Add meta to the route

This project uses a centralized configuration route. I added the meta attribute. The existence means that this is a route that needs to be kept Alive, which means that it is only when it is currently going to this route and needs to be cached.

const routes = [
    ...
    {
        name: 'Commodity Management (Mall Products)', 
        path: '/web/supplier/goods/mallgoodsmgr',
        component: './supplier/goods/goodsManage',
        meta: {
          keepAlive: {
            toPath: '/web/supplier/goods/mallgoodsmgr/detail', // Only when you go to the details page, you need to cache the product management (mall product) route          },
        },
    }
    ...
]

3. Rendering in the root component

In the root component, wrap the entire application with <AliveScope/> and wrap the pages that need to be cached with <KeepAlive/>. This part of the document is written in <App/>, and if it is umi, it can be written in layouts.
Through the flattening calculation of tree, all routes with keepAliveRoutes are obtained. By judging that if the current page needs to keepAlive, it needs to be wrapped with <KeepAlive/>.

import KeepAlive, { AliveScope, useAliveController } from 'react-activation'

// tree flattenedfunction treeToList(tree, childrenKey = 'routes') {
  var queen = []
  var out = []
  queen = (tree)
  while () {
    var first = ()
    if (first[childrenKey]) {
      queen = (first[childrenKey])
      delete first[childrenKey]
    }
    (first)
  }
  return out
}

// Get all routes from the routes route tree: keepAliveRoutesconst allFlatRoutes = treeToList(routes) // All routesconst keepAliveRoutes = ((item) =&gt; ?.keepAlive) // keepAlive's routing
function Index(props) {
  const location = useLocation()
  
  const routeItem = (
    (item) =&gt;  == 
  ) // from page
  let dom = 
  if (routeItem) {
    dom = &lt;KeepAlive id={}&gt;{}&lt;/KeepAlive&gt; // Is it necessary to add id. Otherwise, the page of keepAlive will jump to another page of keepAlive. There will be problems.  }

  return (
    &lt;AliveScope&gt;
      &lt;div className={styles.page_container}&gt;{dom}&lt;/div&gt;
    &lt;/AliveScope&gt;
  )
}

Note that if AliveScope contains multiple KeepAlives, <KeepAlive/> must have an id.

4. Cache when the specified page is redirected

After the previous step, although the page is cached, any page it jumps to will be cached. We need to cache only when the specified page is jumped.
My method is

If the page that jumps happens to be its own, then do not do anything (because at this time this page has been wrapped in KeepAlive and is in a cached state)
If it is not its own, call the clear method and clear the cache

4.1 Clear method

react-activation provides useAliveController to manually control the cache, where the clear method is used to clear KeepAlive in all caches

4.2 Use status management to record toPath

Listen to history, use state management (I use dva) to record the page you are about toPath
I record the pages the app is about to go to through dva

const GlobalModel = {
  namespace: 'global',

  state: {
    /**
     * keepAlive
     */
    toPath: '',
    keepAliveOptions: {}, // The options passed to keepAlive page  },

  effects: {},

  reducers: {
    save(state, { payload }) {
      return {
        ...state,
        ...payload,
      }
    },
    setToPath(state, { payload }) {
      return {
        ...state,
        toPath: payload,
      }
    },
  },

  subscriptions: {
    setup({ history, dispatch }) {
      // Subscribe history(url) change, trigger `load` action if pathname is `/`
      ((route, typeStr) =&gt; {
        const { pathname } = route
        dispatch({
          type: 'setToPath',
          payload: pathname,
        })
      })
    },
  },
}

4.3 Add useEffect to the root component
The root component reads the toPath page to be accessed from dva, and then adds a useEffect. If the to-go page is not the current route itself, execute the clear method provided by react-activation.

...

function Index(props) {
  const location = useLocation()
  const toPath =  // Get the page you will visit from dva  
  const routeItem = (
    (item) =&gt;  == 
  ) // from page  
  
  /// Add new code  /// Add new code  /// Add new code  useEffect(() =&gt; {
    ('toPath Change', toPath)

    // from page is the page that needs to keepAlive    if (routeItem) {
      ('from page is the page that needs to keepAlive', routeItem)
      if (toPath == ?.) {
        // The page I went to happen to be the current route        ('The page I went to happen to be the current route, so I don't do anything')
      } else {
        ('clear')
        if (aliveController?.clear) {
          ()
        }
      }
    }
  }, [toPath])
  /// Add new code end
  let dom = 
  if (routeItem) {
    dom = &lt;KeepAlive id={}&gt;{}&lt;/KeepAlive&gt; // Is it necessary to add id. Otherwise, the page of keepAlive will jump to another page of keepAlive. There will be problems.  }

  return (
    &lt;AliveScope&gt;
      &lt;div className={styles.page_container}&gt;{dom}&lt;/div&gt;
    &lt;/AliveScope&gt;
  )
}
export default connect(({ global, login }) =&gt; ({ global, login }))(Index)

4.4 Optimization

Now there is a problem: when jumping from list A, then jumping from list B, and then jumping from list A, A will not refresh:
List A => Details Page => List B => List A At this time, List A is not refreshed or blank.
Because when we came out of the details page (jumped to list B), we did not clear the cache of list A.
So you need to check whether the current page is a toPath page that requires a keepAlive page.

Root Component:

function Index(){
  ...
  
  const parentItem = ((item) =&gt; ?.keepAlive?.toPath == ) // parentItem exists toPath of the current page.
  useEffect(() =&gt; {
    ('toPath Change', toPath)

    ...
    
    /// Add new code    /// Add new code    /// Add new code    // from page is the toPath of a keepAlive page    if (parentItem) {
      ('from page is the toPath of a keepAlive page, parentItem', parentItem)
      if (toPath == ) {
        // The page you went to is        ('The page I went to is, nothing to do')
      } else {
        ('clear')
        if (aliveController?.clear) {
          ()
        }
      }
    }
  }, [toPath])
  
  ...
}

5. Extract logic to custom hooks

import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import KeepAlive, { AliveScope, useAliveController } from 'react-activation'
import routes from '../../config/'

// tree flattenedfunction treeToList(tree, childrenKey = 'routes') {
  var queen = []
  var out = []
  queen = (tree)
  while () {
    var first = ()
    if (first[childrenKey]) {
      queen = (first[childrenKey])
      delete first[childrenKey]
    }
    (first)
  }
  return out
}

const allFlatRoutes = treeToList(routes) // All routesconst keepAliveRoutes = ((item) =&gt; ?.keepAlive) // keepAlive's routing
function index(props) {
  const location = useLocation()

  // keep alive
  const aliveController = useAliveController()

  const toPath =  // The page to be visited  const routeItem = ((item) =&gt;  == ) // from page  const parentItem = ((item) =&gt; ?.keepAlive?.toPath == )

  useEffect(() =&gt; {
    ('toPath Change', toPath)

    // from page is the page that needs to keepAlive    if (routeItem) {
      ('from page is the page that needs to keepAlive', routeItem)
      if (toPath == ?.) {
        // The page I went to happen to be the current route        ('The page I went to happen to be the current route, so I don't do anything')
      } else {
        ('clear')
        if (aliveController?.clear) {
          ()
        }
      }
    }

    // from page is the toPath of a keepAlive page    if (parentItem) {
      ('from page is the toPath of a keepAlive page, parentItem', parentItem)
      if (toPath == ) {
        // The page you went to is        ('The page I went to is, nothing to do')
      } else {
        ('clear')
        if (aliveController?.clear) {
          ()
        }
      }
    }
  }, [toPath])

  return {
    fromIsNeedKeepAlive: routeItem,
  }
}

export default index

The root component only needs to introduce this hooks:

function Index(props) {
  const location = useLocation()

  const { fromIsNeedKeepAlive } = useKeepAliveLayout(props) // Key code Key code Key code
  let dom = 
  if (fromIsNeedKeepAlive) {
    dom = &lt;KeepAlive id={}&gt;{}&lt;/KeepAlive&gt; // Is it necessary to add id. Otherwise, the page of keepAlive will jump to another page of keepAlive. There will be problems.  }

  return (
    &lt;AliveScope&gt;
      &lt;div className={styles.page_container}&gt;{dom}&lt;/div&gt;
    &lt;/AliveScope&gt;
  )
}

6. When returning to the list page from the details page, control whether the list page is refreshed, that is, return to the parameter transfer.

Now there is only this last problem left, which is actually the keepAlive page and goBack parameter transmission problem

Ideas:

  • Add a keepAliveOptions object to state management, which is the parameter passed to the list page by the details page.
  • When executing goBack on the details page, call status management dispatch to modify keepAliveOptions
  • List page listens to keepAliveOptions, if keepAliveOptions changes, execute the incoming method

import { useEffect } from 'react'
import { useDispatch, useStore } from 'dva'
import { router } from 'umi'

/**
 * @description KeepAlive page, when there are parameters passed, you can use this monitor to listen to it.
 * @param {(options:object)=>void} func
 */
export function useKeepAlivePageShow(func) {
  const dispatch = useDispatch()
  const store = useStore()
  const state = ()
  const options =  ?? {}

  useEffect(() =&gt; {
    func(options) // implement    return () =&gt; {
      ('KeepAlive page cache uninstall')
      dispatch({
        type: 'global/save',
        payload: {
          keepAliveOptions: {},
        },
      })
    }
  }, [(options)])
}

/**
 * @description PageA (keepAlive page) went to PageB. When you want to pass parameters to PageA from PageB goBack, you need to use this method
 * @returns {(params:object)=>void}
 */
export function useKeepAliveGoback() {
  const dispatch = useDispatch()

  function goBack(parmas = {}) {
    dispatch({
      type: 'global/save',
      payload: {
        keepAliveOptions: parmas,
      },
    })
    ()
  }

  return goBack
}

use:

Details

import { useKeepAliveGoback } from '@/hooks/useKeepAliveOptions'

function Index(){
    ...
    const keepAliveGoback = useKeepAliveGoback() // Used to pass parameters to the page of keepAlive on the previous page    ...
    
    return (
        &lt;&gt;
            ...
            &lt;button onClick={() =&gt; {
                keepAliveGoback({ isAddSuccess: true }) // Pass options to the list page            }&gt;&lt;/button&gt;
            ...
        &lt;/&gt;
    )
}

List page

import { useKeepAlivePageShow } from '@/hooks/useKeepAliveOptions'

function Index(){
    ...
    // options: isAddSuccess isEditSuccess
    useKeepAlivePageShow((options) =&gt; {
        ('keepAlive options', options)
        if () {
          // New creation is successful // List page number becomes 1 and refreshed          search()
        } else if () {
          // Edit successfully // The list page number remains unchanged and refreshed          getData()
        }
    })
    
    ...
    
    return &lt;&gt;...&lt;/&gt;
}

Related Documents

react-activation
dva documentation

This is the article about using react-activation to implement keepAlive to support returning parameters. This is the end of this article. For more related react-activation to keepAlive to return to parameters, please search for my previous articles or continue to browse the related articles below. I hope everyone will support me in the future!