Code Monkey home page Code Monkey logo

react-router-cache-route's Introduction

CacheRoute

size dm

English | 中文说明

Route with cache for react-router like keep-alive in Vue.

Online Demo

If you want <KeepAlive /> only, try react-activation

React v15+

React-Router v4+



Problem Scenarios

Using Route, component can not be cached while going forward or back which lead to losing data and interaction


Reason & Solution

Component would be unmounted when Route was unmatched

After reading source code of Route we found that using children prop as a function could help to control rendering behavior.

Hiding instead of Removing would fix this issue.

https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Route.js#L41-L63


Install

npm install react-router-cache-route --save
# or
yarn add react-router-cache-route

Usage

Replace Route with CacheRoute

Replace Switch with CacheSwitch (Because Switch only keeps the first matching state route and unmount the others)

import React from 'react'
import { HashRouter as Router, Route } from 'react-router-dom'
import CacheRoute, { CacheSwitch } from 'react-router-cache-route'

import List from './views/List'
import Item from './views/Item'

const App = () => (
  <Router>
    <CacheSwitch>
      <CacheRoute exact path="/list" component={List} />
      <Route exact path="/item/:id" component={Item} />
      <Route render={() => <div>404 Not Found</div>} />
    </CacheSwitch>
  </Router>
)

export default App

CacheRoute props

name type default description
when String / Function "forward" Decide when to cache
className String - className prop for the wrapper component
behavior Function cached => cached ? { style: { display: "none" }} : undefined Return props effective on the wrapper component to control rendering behavior
cacheKey String / Function - For imperative control caching
multiple (React v16.2+) Boolean / Number false Allows different caches to be distinguished by dynamic routing parameters. When the value is a number, it indicates the maximum number of caches. When the maximum value is exceeded, the oldest updated cache will be cleared.
unmount (UNSTABLE) Boolean false Whether to unmount the real dom node after cached, to save performance (Will cause losing the scroll position after recovered, fixed with saveScrollPosition props)
saveScrollPosition (UNSTABLE) Boolean false Save scroll position

CacheRoute is only a wrapper component that works based on the children property of Route, and does not affect the functionality of Route itself.

For the rest of the properties, please refer to https://reacttraining.com/react-router/


About when

The following values can be taken when the type is String

  • [forward] Cache when forward behavior occurs, corresponding to the PUSH or REPLACE action in react-router
  • [back] Cache when back behavior occurs, corresponding to the POP action in react-router
  • [always] Always cache routes when leave, no matter forward or backward

When the type is Function, the component's props will be accepted as the first argument, return true/false to determine whether to cache.


CacheSwitch props

name type default description
which Function element => element.type === CacheRoute <CacheSwitch> only saves the first layer of nodes which type is CacheRoute by default, which prop is a function that would receive a instance of React Component, return true/false to decide if <CacheSwitch> need to save it, reference #55

Lifecycles

Hooks

use useDidCache and useDidRecover to inject customer Lifecycle didCache and didRecover

import { useDidCache, useDidRecover } from 'react-router-cache-route'

export default function List() {

  useDidCache(() => {
    console.log('List cached 1')
  })

  // support multiple effect
  useDidCache(() => {
    console.log('List cached 2')
  })

  useDidRecover(() => {
    console.log('List recovered')
  })

  return (
    // ...
  )
}

Class Component

Component with CacheRoute will accept one prop named cacheLifecycles which contains two functions to inject customer Lifecycle didCache and didRecover

import React, { Component } from 'react'

export default class List extends Component {
  constructor(props) {
    super(props)

    props.cacheLifecycles.didCache(this.componentDidCache)
    props.cacheLifecycles.didRecover(this.componentDidRecover)
  }

  componentDidCache = () => {
    console.log('List cached')
  }

  componentDidRecover = () => {
    console.log('List recovered')
  }

  render() {
    return (
      // ...
    )
  }
}

Drop cache

You can manually control the cache with cacheKey prop and dropByCacheKey function.

import CacheRoute, { dropByCacheKey, getCachingKeys } from 'react-router-cache-route'

...
<CacheRoute ... cacheKey="MyComponent" />
...

console.log(getCachingKeys()) // will receive ['MyComponent'] if CacheRoute is cached which `cacheKey` prop is 'MyComponent'
...

dropByCacheKey('MyComponent')
...

Clear cache

You can clear cache with clearCache function.

import { clearCache } from 'react-router-cache-route'

clearCache()

react-router-cache-route's People

Contributors

cjy0208 avatar dependabot[bot] avatar ivocin avatar wilk avatar xuandiboss avatar xwinstone avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-router-cache-route's Issues

demo

可以给demo吗

Object.assign is not a function

/**
* Only children prop of Route can help to control rendering behavior
* 只有 Router 的 children 属性有助于主动控制渲染行为
*/
React__default.createElement(reactRouterDom.Route, _extends({}, __rest__route__props, {
children: function children(props) {
return React__default.createElement(
CacheComponent,
_extends({}, props, { when: when, className: className, behavior: behavior }),
function (cacheLifecycles) {
return React__default.createElement(Updatable, {
match: props.match,
render: function render() {

                Object.assign(props, { cacheLifecycles: cacheLifecycles });

                if (component) {
                  return React__default.createElement(component, props);
                }

我看源码中已经对Object.assign做了兼容处理,这边最后render函数里面的Object.assign为什么不使用_estends函数替代?我在低版本的浏览器运行时出现Object.assign is not a function错误

引入antd-pro的脚手架里无效?

你好,我发现这个用在阿里那个antd-pro的脚手架里还是不起作用,看了下代码 可能是跟dva框架的按需加载有关,是不是用了这个就不能使用按需加载了?

能否支持一下 自定义 传 history 对象

新版的 react-router-dom 好像没法传 自定义 的 history 对象,有些项目需要集中的路由管控,然后和 react-router-dom 不是同一个 history 对象,导致所有的 action 总是 POP ,希望能支持一下

ts报错

"react-router-cache-route": "^1.8.1"
"@types/react": "16.4.18"
"typescript": "3.1.6"

  1. CacheRoute设置when="always"时:
    TS2322: Type '"always"' is not assignable to type 'When | ((props: CacheRouteProps) => boolean)'.
    改为when={()=>true}就好;
  2. TS2604: JSX element type 'CacheSwitch' does not have any construct or call signatures.

react-router-cache-route在umi框架里怎么使用啊

在之前dva-cli脚手架生成的框架里,有一个可以由我们自定义配置的router.js,所以我们可以import react-router-cache-route去配置缓存路由,但是在umi里不管是约定式路由还是配置的路由,其最终生成有效的路由文件的过程都是框架自己做的,我该在哪一步去引入 react-router-cache-route配置缓存路由呢?

是否只能用于HashRouter ?可否兼容Router的方式

以下方式似乎无法缓存,hashHistory是用history库创建的
import { Router, Route} from 'react-router-dom'
<div className='app-main'>
<Router history={hashHistory}>
<CacheRoute path='/list/public-fund' component={PublicFundList} when='back'/>
</Router>
</div>

希望能添加配置来设置最大缓存数量

有时候页面栈长度太大会导致缓存的页面越来越多,可能会引起性能问题,希望添加属性来配置最大缓存数量,超出数量时,移除最早被缓存的页面(页面栈底部)。

我尝试用getCachingKeys()来获取缓存的页面,然后用dropByCacheKey()来清除,但是发现getCachingKeys()获取的列表并不是按照时间顺序来排列的,所以还是希望库本身能支持这个功能。

Uncaught Invariant Violation

Uncaught Invariant Violation: CacheSwitch.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.

用router组件的时候是好的,用cacheSwitch就会报错,知道原因么

CacheRoute inside another Function Component

I commend this library because it saves a lot of new users from having to be forced to learn redux just to cater to this router issue.

My issue is because I created a functional component called ProtectedRoute where it returns the CacheRoute or a redirect if the user is not authenticated.

I place the ProtectedRoute tag in the CacheSwitch but it does not work. any idea what could be wrong with this code? Thank you

const ProtectedRoute = (props) => {
    return localStorage.getItem('user') !== null
        ? <CacheRoute {...props} />
        : <Redirect to="/login"/>;
};

export default ProtectedRoute;

And this is the cache switch

<CacheSwitch>
              <ProtectedRoute component={MyComponent} path="/" exact/>
</CacheSwitch>

CacheSwitch报错

react-router-cache-route V1.3.1,react V15.2.1,react-router [V4.3.1,CacheSwitch报错如下:
Uncaught Error: CacheSwitch.render(): A valid React element( or null ) must be returned. You may returned undefined, an array or some other invalid object.

关于CacheRoute,能否在匹配路由时,动态确定是否加载缓存?

目前开发的后台管理系统界面,采用tab标签管理多个打开的页面,加载页面有以下几种:
1.点击侧边菜单,或者tab页之间切换时,需要加载已缓存的相同路由页面。
2.当打开某项数据的详情页或编辑页时,需要打开全新的页面,而非缓存页面。

能否增加一个机制,可以动态控制匹配路由时,应加载缓存还是不加载缓存页面呢?

列表页和详情页公用位置

列表页A
详情页B
加入A跳转到B,A之前滚动多少距离到B页面scrollTop就是多少,然后B滚动后,再用push方法跳转到A,A页面现在的位置就是B页面的位置,B页面用goback()方法跳转的时候没有此问题出现。

要求 A 跳转 B 直接问到B的顶部,B会A页面,A还是之前的高度

无法缓存的BUG

我发现一个奇怪的BUG,当一个CacheRoute的子组件是一个Component并且这个Component的内部包含其他CacheRoute,
那么使用render,children属性创建的这些CacheRoute将无法缓存,如附件中的/p2/s1,/p2/s2。而使用component属性创建的CacheRoute可以正常缓存,如附件中的/p2/s3>
test.zip

dropByCacheKey('MyComponent')无效

比如我缓存了两个路由,['/index/aaa', /index]

for (const cacheKey of getCachingKeys()) {
      if( cacheKey.includes('/index') ) {
        dropByCacheKey(cacheKey)
      }
 }

这样写应该可以吧,可是下次点击还是会显示之前缓存的状态,检查元素页面还是在display之间切换,是不是我哪里写法有问题?
😂求大佬帮忙

返回页面swiper不再轮播

你好,我首页有一个轮播图,正常可以自动轮播。但是点击详情页,返回首页的时候轮播图组件就不再会自动轮播了,我用的swiper4还有andt-mobile的轮播图,路由用默认缓存方式。是否跟你的路由display:none有关?路由不断前进,这种隐藏方式会不会造成dom树越累越多,最终会有卡顿?

关于className的完全控制

hi,这是一个非常棒的东西。

但我想说说我遇到的情况:
现在的处理方式是添加包裹组件,设置display:none来实现的keep-alive
但display:none会导致内部的一些子组件丢失高度,某些子组件的滚动条就会重置到顶部

目前组件有个className可以设置。
我更希望组件不要去设置display:none,而是我能自己定义className, 来完全控制怎么隐藏。
组件能帮我在componentDidCache时添加这个className, 在componentDidRecover时去掉这个className
就太棒了

比如我现在的需求,我需要这么隐藏:

className {
 position: absolute;
 top:0;
 left:0;
 visibility: hidden;
 z-index: -1;
}

以上是我的一个建议... 望考虑

希望能在何时缓存时自定义化

选择缓存功能的when只能选“always”,“forward”,“back”,forward是通过history.action是否是‘pop’来判断,也就是说如果我用react-router中的goBack()才会让页面缓存
我希望能自定义化,在我调用指定history.push() 时让他缓存住

在 electron-react-boilerplate下使用没效果

你好,在electron 下使用该控件发现push后,上一个控件一样会调用 componentWillMount 并且被清除掉,数据和滚动位置都没有保存到。

<Route .../>

已经改成

<CacheRoute ... />

另外 didCache 和 didRecover 调用正常

支持按需加载页面

您好,能否支持按需加载页面,之前引用组件路径使用的是bundle-loader/..., 引用react-router-cache-route后,引用组件时必须使用 相对路径

componentWillReceiveProps has been renamed Issue

Hi, Thanks for make this library.
Caching effectively work in my app.
But, repeatedly occur 'componentWillReceiveProps' warning in my app.
Warning is as follows :

react-dom.development.js:12466 Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.

* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

Please update the following components: CacheComponent

I think present version using componentWillReceiveProps.
I hope to improve this Warning in the next version.
Thank you!

缓存失效的bug

开发过程中遇到一个二级菜单不缓存的问题,解决了很久才解决,很容易找错方向,记录下来给以防后面的人遇到时无从下手。

function createRoute(path, component, moduleCode) {
  const Cmp = asyncRouter(
    component,
    null,
    null,
    moduleCode,
  );
  return <CacheRoute path={path} component={Cmp} />;
}

const Home = asyncRouter(
  () => import('../{{ source }}/containers/components/home'),
  null,
);

export default class AutoRouter extends Component {
  render() {
    return (
      <CacheSwitch>
        <CacheRoute exact path="/" component={Home} />
        {'{{ routes }}'}
        <CacheRoute exact path="/iframe/:name" cacheKey="iframe" component={IFRAMEINDEX} /> 
        <CacheRoute path="*" component={nomatch} />
      </CacheSwitch>
    );
  }
}

asyncRouoter是按需加载组件,类似react的Lazy

因为路由是动态生成的,所以使用了nunjucks模板。编译后替换{{ routes }}

当时发现,直接写在AutoRouter中的路由可以缓存,而routes中的不能缓存,所以觉得是二级路由的缓存匹配可能有问题。

调试了很多,无果,起了个新项目写了个demo,发现多级路由也是可以缓存的,那就把二级的目录直接写到AutoRouter中,发现可以缓存。

所以问题就在creteRoute方法上!

再进一步探索发现createRoute会返回新的component。所以加入缓存解决这个问题。

const routes = {};

function createRoute(path, component, moduleCode) {
  if (!routes[path]) {
    const Cmp = asyncRouter(
      component,
      null,
      null,
      moduleCode,
    );
    routes[path] = <CacheRoute path={path} component={Cmp} />;
  }
  return routes[path];
}

所以大家遇到不缓存的情况,确认没写错的情况下,可以去看看是不是返回了不同的component

react 16.8.6 下报错

WechatIMG117

错误版本

react: 16.8.6
react-router-cache-route: 1.4.1

正常版本

react: 16.6.0
react-router-cache-route: 1.4.1

能让我的子组件不参与缓存吗

我的H5其中一页,我用react-router-cache-route 缓存了,但是我希望其中的一个子组件不参与缓存,这个子组件与父组件的联系是一个点击事件, 能做到吗

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.