React实现keepAlive

keepAlive是什么

keep-alive是vue中的内置组件,使用keep-alive包裹组件可以在该组件的切换过程中将页面的状态缓存在内存中,界面切换后被包裹的组件实例不会被销毁,防止界面重复渲染DOM,并且可以防止在页面切换过程中组件的数据丢失

使用场景

如下图所示,想要实现以下效果:

1.第一步: 在user路由下的输入框中输入内容

在这里插入图片描述

2.第二步: 切换至home路由
在这里插入图片描述

3.第三步:再切换回来,保留上次输入的值
在这里插入图片描述

即:切换路由时缓存之前的组件,保留之前输入的值不被清空

vue项目可以使用内置的keep-alive组件包裹实现。react可以通过目前市面上的其他库来实现,例如React Activationreact-keep-alive等等,我们也可以手动实现。

代码实现

文件目录结构

核心功能实现在keepAlive目录中

/KeepAlive.js
包裹在需要缓存的组件外侧,通过Provider把状态和方法传递给子组件,如果判断出reactElement已经传过来了,但是对应的nodes数组还是为null的话就触发相应的reducer把该nodes重新赋值

import { useCallback, useReducer } from "react"
import {KeepAliveReducer} from './KeepAliveReducer'
import * as actionTypes from './types'
import { KeepAliveContext } from "./KeepAliveContext"

function KeepAlive(props) {
    console.log('KeepAlive props=>', props);
    /**
     * {
     *   home: {
     *      keepAliveId: 'home',
     *      reactElement: reactElement,
     *      nodes: nodes,
     *      status: create | created
     *   },
     *   user: {
     *      keepAliveId: 'user',
     *      reactElement: reactElement,
     *      nodes: nodes,
     *      status: create | created
     *   }
     * }
     */
    const [keepAliveStates, dispatch] = useReducer(KeepAliveReducer, {})
    
    const setKeepAliveState = useCallback(({ reactElement, keepAliveId }) => {
        if(!keepAliveStates[keepAliveId]) {
            dispatch({
                type: actionTypes.CREATING,
                payload: {
                    keepAliveId,
                    reactElement
                }
            })
        }
    }, [keepAliveStates])

    return (
        <KeepAliveContext.Provider value={{
            keepAliveStates,
            setKeepAliveState,
            dispatch
        }}>
        { props.children }
        {
            Object.values(keepAliveStates).map(({ keepAliveId, reactElement }) => (
                <div key={ keepAliveId } ref={(node) => {
                    console.log(keepAliveStates);
                    if(node && !keepAliveStates[keepAliveId].nodes) {
                        dispatch({
                            type: actionTypes.CREATED,
                            payload: {
                                keepAliveId,
                                nodes: [...node.childNodes]
                            }
                        })
                    }
                }}>{ reactElement }</div>
            ))
        }
        </KeepAliveContext.Provider>
    )
}

export default KeepAlive

/keepAliveReducer.js

不同时机更新state的reducer

import * as actionTypes from './types'
/**
 * 
 * @param {*} state keepAliveStates
 * @param {*} action { type, payload }
 */
export function KeepAliveReducer(state, action) {
    const { type, payload } = action
    const { keepAliveId, reactElement, nodes } = payload

    switch(type) {
        case actionTypes.CREATING:
            console.log(state);
            return {
                ...state,
                [keepAliveId]: {
                    keepAliveId,
                    reactElement,
                    status: type,
                    nodes: null
                }
            }
        case actionTypes.CREATED:
            console.log(state);

            return {
                ...state,
                [keepAliveId]: {
                    ...state[keepAliveId],
                    status: type,
                    nodes
                }
            }
        default: return state
    }
}

/keepAliveContext.js

自定义context

import { createContext } from 'react'

export const KeepAliveContext = createContext()

/transfer.js

每次进入判断有无节点数组,没有则触发reducer把当前的组件赋值给state的
reactElement属性,如果有则通过appendChild方法把节点添加至当前组件下

/* eslint-disable react-hooks/rules-of-hooks */
import { useContext, useEffect, useRef } from "react"
import { KeepAliveContext } from "./keepAliveContext"


export function KeepAliveTransfer(KeepAliveComponent, keepAliveId) {

    return function(props) {
        const _ref = useRef(null) 
        const { keepAliveStates, setKeepAliveState } = useContext(KeepAliveContext)
        // console.log('keepAliveStates', keepAliveStates);
        // console.log('setKeepAliveState', setKeepAliveState);
        useEffect(() => {
            const state = keepAliveStates[keepAliveId]
    
            if(state && state.nodes) {
                console.log('有节点', state);
                state.nodes.forEach(node => _ref.current.appendChild(node))
            } else {
                console.log('无节点', state);
                setKeepAliveState({
                    reactElement: <KeepAliveComponent { ...props }/>,
                    keepAliveId
                })
            }
        }, [keepAliveStates, setKeepAliveState, props])
        return (
            <div ref={ _ref }></div>
        )
    }
}


/types.js

export const CREATING = 'CREATING'
export const CREATED = 'CREATED'

源码自取-https://gitee.com/mountain-casserole/react-keep-alive

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
React中没有内置的TabPane组件缓存功能类似于Vue的keep-alive。但是我们可以通过以下方法来实现类似的缓存效果。 一种方法是利用React的状态管理机制,结合条件渲染来实现TabPane的缓存。具体步骤如下: 1. 在TabPane组件的父组件中,创建一个状态变量用于标识当前选择的Tab。 2. 在TabPane组件中,根据该状态变量的值来决定是否渲染该Tab内容。 3. 当切换Tab时,只修改状态变量的值而不重新渲染TabPane组件,以达到缓存的效果。 示例代码如下: ```jsx import React, { useState } from 'react'; const App = () => { const [currentTab, setCurrentTab] = useState(0); const handleTabChange = (index) => { setCurrentTab(index); }; return ( <div> <ul> <li onClick={() => handleTabChange(0)}>Tab 1</li> <li onClick={() => handleTabChange(1)}>Tab 2</li> <li onClick={() => handleTabChange(2)}>Tab 3</li> </ul> <div> {currentTab === 0 && <TabContent1 />} {currentTab === 1 && <TabContent2 />} {currentTab === 2 && <TabContent3 />} </div> </div> ); }; const TabContent1 = () => { return <div>Tab 1 Content</div>; }; const TabContent2 = () => { return <div>Tab 2 Content</div>; }; const TabContent3 = () => { return <div>Tab 3 Content</div>; }; export default App; ``` 通过上述方法,只有当前选择的Tab内容会被渲染,而其他Tab的内容则会被缓存,达到类似Vue的keep-alive的效果。 另外,还可以使用第三方库如`react-tabs`或`react-router`等来实现类似的Tab缓存功能,这些库提供了更丰富的Tab组件并且内置了缓存功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值