了解React中的本地缓存

894 阅读9分钟

ReactRedux通常一起用来构建应用和管理应用状态。然而,使用Redux的一个缺点是,在页面刷新时,包含应用程序数据的整个Redux状态被重置。重新初始化Redux状态往往意味着必须重复调用API来重新获取数据,或者完全丢失用户数据。

在这篇文章中,我们将通过一个小例子来演示Redux状态如何在本地进行缓存,然后在页面刷新时使用缓存数据进行补给。

前提条件

在本教程中,你需要一个安装了Redux DevTools扩展的谷歌Chrome浏览器。这个扩展可以在Chrome Web Store中找到。

缓存实现

本教程将演示如何在比萨饼订购表中实现本地缓存。想象一下,你正在为晚餐订购比萨饼,但在中途被打断,决定查看你的电子邮件。当你检查完电子邮件并再次加载网站时,如果你之前的输入还在,那就太好了。本地缓存将帮助你解决这样的情况,所以用户将有更好的体验,不需要重新输入。

初始设置

访问CodeSandbox这里,渲染使用React、Redux和Twilio的用户体验平台Twilio Paste构建的Pizza Form应用程序。为了更好地访问,点击CodeSandbox浏览器窗口中最右边的图标,在一个新的标签页中打开Pizza Form。

CodeSandbox Pizza Form

接下来,在Pizza Form上点击右键,选择 "检查 "来打开开发者工具窗口。从开发者工具的顶部栏中选择 "Redux "选项,打开Redux DevTool。你可能需要点击">>"图标来查看所有的选项并找到最近添加的扩展。

Inspect element image of pizza form

在Redux DevTool内部,从右上方的菜单中选择 "状态"。树下的文字显示了表格的分类。开始填写Pizza表格,然后点击form 前面的小三角形,再对pizzavalues

当你继续填写表格时,注意Redux状态如何变化以反映你的输入。

Video of Redux State Changing

接下来,刷新Pizza表格页面,观察Redux状态如何被重置,你的所有输入都会丢失。

Sad Pizza Face

我们将实现缓存,这样就可以保留用户的输入,你就不必再次输入所有的比萨饼细节。

将数据保存到缓存中

我们的本地缓存将来自money-clip库,它是使用IndexedDB--一种浏览器存储实现的。导航回到CodeSandbox的代码部分,打开_index.js_文件。就在定义rootEl 的地方下面,添加以下一行。

const cache = getConfiguredCache();

现在缓存已被初始化,下一步是将数据保存到缓存中。我们将用redux-persist-middleware库来做这件事,它创建的中间件可以在提供的Redux动作上触发一个缓存函数。这意味着第一步是决定哪个Redux动作应该触发保存到缓存中。

回到Pizza Form窗口,确保在Redux DevTool的左上角菜单中选择了 "Inspector",并且在右上角选择了 "Action"。填写Pizza表格,注意每次修改表单字段时,都会生成一个新的@@redux-form/CHANGE 动作,并显示在左侧的侧边栏。

随意点击侧边栏中的任何一个动作,看看Redux动作到底包含什么。

Redux Action Content

由于我们试图存储最新的用户输入,我们希望在每个@@redux-form/CHANGE 动作上缓存Redux状态。幸运的是,redux-persist-middleware库提供了这个功能。我们所要做的就是定义我们想要保存的动作和我们想要保存的内容。在_index.js_中的cache 定义下添加以下代码。

const actionMap = {
 "@@redux-form/CHANGE": ["form"]
};

actionMap 中的键是应该触发缓存功能的Redux动作。actionMap 的值是我们想保存在缓存中的Redux状态的部分。我们传入“form” ,因为这是我们的reducer(管理Redux状态的函数)安装的名字,因此也是我们Redux状态中的键。

下一步是在_index.js_文件中定义Redux中间件。这将接收actionMap 对象,并执行必要的动作来触发缓存功能。复制并粘贴以下代码在_index.js_中的actionMap 初始化下。

const persistMiddleware = getPersistMiddleware({
 cacheFn: cache.set,                    // specifies we would like to set the cache
 logger: console.info,                   // adds helpful console logging
 actionMap                              // contains the Redux action and key we specified
});  

通过修改存储初始化为读取,将中间件添加到我们现有的Redux存储中。

const store = createStore(
   reducer,
   compose(
     applyMiddleware(persistMiddleware),
     window.__REDUX_DEVTOOLS_EXTENSION__ &&
       window.__REDUX_DEVTOOLS_EXTENSION__()
   )
 );

请注意,我们在第四行加入了中间件。现在,如果你在开发工具中打开 "控制台 "标签填写表单字段,你可以看到每次你修改字段时,都会设置缓存。

GIF of console log cache

要查看缓存,请切换到开发工具窗口中的 "应用程序 "选项卡。在左边的侧边栏中找到 "存储",双击 "IndexedDB "来展开细节。再次双击 "keyval-store",然后选择 "keyval",查看你在缓存中的输入。如果没有更新,点击 "keyval-store",选择 "刷新数据库",然后再次检查。

Cache View

下面是一个输出结果可能是什么样子的例子。

Cache View Output

从缓存中加载数据

即使数据在缓存中,刷新页面仍然会显示一个空白表单。这是因为在初始化时,数据还没有从缓存中加载到Redux中。做到这一点的方法是用下面的调用来包装位于_index.js_底部的store 定义和ReactDOM.render 语句。请注意,createStore() 的调用已经被修改,以纳入缓存数据。

cache.getAll().then((data) => {         // fetches all data from the cache
  const store = createStore(
    reducer,
    data,                               // passes in the cache data
    compose(
      applyMiddleware(persistMiddleware),
      window.__REDUX_DEVTOOLS_EXTENSION__ &&
      window.__REDUX_DEVTOOLS_EXTENSION__()
    )
  );
  
  ReactDOM.render(
    <Provider store={store}>
      <Theme.Provider theme="default">
      <PizzaForm />
      </Theme.Provider>
    </Provider>,
    rootEl
  );
});

在这一步之后,Redux商店被初始化为缓存数据。如果你现在填写Pizza表格并刷新......你的响应仍然在那里!!。

Pizza Form on Refresh

从缓存中删除数据

有些时候,我们不想再把用户的响应存储在本地,比如当用户点击Clear ValuesSubmit按钮的时候。目前,如果我们点击Clear Values并刷新页面,表单会重新填入我们清除的输入,因为它是从缓存中加载的。

我们通过重复保存到缓存中的相同过程来解决这个问题。

  • 挑选要删除的Redux动作
  • 将一个Redux动作映射到Redux状态中
  • 创建包含映射和缓存功能的中间件
  • 将中间件应用到我们的Redux商店

打开Redux DevTools,实验一下提交或清除表单,注意这两个动作都会触发@@redux-form/RESET 。因此,我们将在_index.js_中定义一个新的地图,该动作位于actionMap 的定义之下。

const clearActionMap = {
 "@@redux-form/RESET": ["form"]
};

同样,clearActionMap 中的键是要执行缓存功能的Redux动作,而值是我们想从缓存中删除的Redux状态的一部分。

接下来,我们将把clearActionMap 传递给_index.js_中定义的一个新的中间件。注意,缓存函数是cache.del ,而不是cache.set 。在persistMiddleware 的定义下添加以下几行。

const clearMiddleware = getPersistMiddleware({
 cacheFn: cache.del,                 // specifies we would like to delete from the cache
 logger: console.info,              // adds helpful logging
 actionMap: clearActionMap          // contains the Redux action and key we specified
});

在存储初始化里面,添加clearMiddleware 作为参数,这样applyMiddleware() 的调用就会变成这样。

compose(
      applyMiddleware(persistMiddleware, clearMiddleware),
      window.__REDUX_DEVTOOLS_EXTENSION__ &&
      window.__REDUX_DEVTOOLS_EXTENSION__()
);

有了这四个步骤,我们就可以了!在清除数值或提交后再刷新时,表单是空白的,与以前不同。通过查看每次按提交清除数值时的控制台日志语句来玩玩这个新功能。

查看开发者工具控制台中的 "应用程序 "选项卡,观察这些操作后缓存是如何被清空的。你可能要点击 "keyval-store "下的 "Refresh Database",才能看到更新的、空的缓冲区。

Empty Fridge

在缓存中添加版本和maxAget

在缓存中添加版本号使我们对数据有了更多的控制。当数据的形状发生变化时,版本号可以被撞开,所以格式不正确的数据永远不会从缓存中加载。版本号还可以包括用户的ID,使缓存的数据具有用户特性。

在_index.js_的顶部重新定义cache ,用下面几行代码传入cacheOptions

const cacheOptions = {
 version: 0.1,
};
const cache = getConfiguredCache(cacheOptions);

注意,如果你填写了表格,在_index.js_中改变了版本号,然后刷新了表格,输入字段就不会被填充。因为我们改变了缓存版本,在初始化时调用cache.getAll() ,出现了版本不匹配,旧的数据被删除。你可以通过检查 "keyval "缓存数据来确认这一点。

缓存控制的另一个方面是数据过期。例如,如果所有的用户都在24小时后自动注销,你可能想在24小时后也删除他们的缓存数据。这里,我们将把这个时间缩短为10秒。在cacheOptions ,添加突出显示的一行。

const cacheOptions = {
  version: 0.1,
  maxAge: ms.seconds(10),
};

现在,你可以看到,如果你填写了表格并在10秒内刷新,表格仍然会被填充。但是,如果你等待10秒后再刷新,缓存数据就会过期,表单就会是空的!你可以通过检查来再次确认这一点。你可以通过用开发者工具检查缓存再次确认这一点。

本地缓存的下一步是什么?

恭喜你实现了文本表单的本地缓存如果你想查看参考实现,完整的代码可以在我的GitHub仓库找到。

本地缓存为更多的用例打开了大门,比如处理一个有20个输入字段的表单,而不是7个。缓存在这种情况下会更有用。

另一个有趣的扩展是缓存API响应,以避免在你的应用程序中调用API,甚至可能允许它离线运行。

我希望你觉得这篇文章很有趣,并了解到更多关于我们如何利用缓存来使我们的应用程序更上一层楼的信息

Pizza Cowboy

Christina Leung是事件流团队的一名软件工程师实习生。她喜欢用技术解决问题,可以通过chleung [at] twilio.com或在 LinkedIn__.