如何从 localStorage 读取 DraftJS 状态?

Posted

技术标签:

【中文标题】如何从 localStorage 读取 DraftJS 状态?【英文标题】:How to read DraftJS state from localStorage? 【发布时间】:2019-08-16 16:38:00 【问题描述】:

我在从 localStorage 读取 Draft.js 原始内容时遇到问题。

我想使用之前存储的 rawContent 作为我的 initialState 用于 reducer。

我认为convertFromRaw 函数是我的问题。 我确实默默地迷恋(不登录到控制台),但我不知道如何解决这个问题。

我在 localStorage 中保留 Draft.js 原始内容:

const contentState = editorState.getCurrentContent();
const rawContent = convertToRaw(contentState);
window.localStorage.setItem(
  "rawContent",
  JSON.stringify(convertToRaw(rawContent))
);

然后当我试图恢复它时,我遇到了我的问题。 我认为convertFromRaw

const rawContent = window.localStorage.getItem("rawContent");

if (rawContent) 
  this.state.editorState = EditorState.createWithContent(
    convertFromRaw(JSON.parse(rawContent))
  );
 else 
  this.state.editorState = EditorState.createEmpty();

我对此的临时解决方案是使用第三方转换器。 在我的例子中,我使用了来自draft-js-export-markdown / draft-js-import-markdownstateToMarkdown / stateFromMarkdown

我的 package.json


  "name": "react-boilerplate",
  "version": "3.7.0",
  "description": "A highly scalable, offline-first foundation with the best DX and a focus on performance and best practices",
  "repository": 
    "type": "git",
    "url": "git://github.com/react-boilerplate/react-boilerplate.git"
  ,
  "engines": 
    "npm": ">=5",
    "node": ">=8.10.0"
  ,
  "author": "Max Stoiber",
  "license": "MIT",
  "scripts": 
    "analyze:clean": "rimraf stats.json",
    "preanalyze": "npm run analyze:clean",
    "analyze": "node ./internals/scripts/analyze.js",
    "extract-intl": "node ./internals/scripts/extract-intl.js",
    "npmcheckversion": "node ./internals/scripts/npmcheckversion.js",
    "preinstall": "npm run npmcheckversion",
    "prebuild": "npm run build:clean",
    "build": "cross-env NODE_ENV=production webpack --config internals/webpack/webpack.prod.babel.js --color -p --progress --hide-modules --display-optimization-bailout",
    "build:clean": "rimraf ./build",
    "start": "cross-env NODE_ENV=development node server",
    "start:tunnel": "cross-env NODE_ENV=development ENABLE_TUNNEL=true node server",
    "start:production": "npm run test && npm run build && npm run start:prod",
    "start:prod": "cross-env NODE_ENV=production node server",
    "presetup": "npm i chalk shelljs",
    "setup": "node ./internals/scripts/setup.js",
    "clean": "shjs ./internals/scripts/clean.js",
    "clean:all": "npm run analyze:clean && npm run test:clean && npm run build:clean",
    "generate": "plop --plopfile internals/generators/index.js",
    "lint": "npm run lint:js",
    "lint:css": "stylelint './app/**/*.js'",
    "lint:eslint": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts",
    "lint:eslint:fix": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts --fix",
    "lint:js": "npm run lint:eslint -- . ",
    "lint:staged": "lint-staged",
    "pretest": "npm run test:clean && npm run lint",
    "test:clean": "rimraf ./coverage",
    "test": "cross-env NODE_ENV=test jest --coverage",
    "test:watch": "cross-env NODE_ENV=test jest --watchAll",
    "coveralls": "cat ./coverage/lcov.info | coveralls",
    "prettify": "prettier --write"
  ,
  "jest": 
    "collectCoverageFrom": [
      "app/**/*.js,jsx",
      "!app/**/*.test.js,jsx",
      "!app/app.js",
      "!app/global-styles.js",
      "!app/*/*/Loadable.js,jsx"
    ],
    "coverageThreshold": 
      "global": 
        "statements": 50,
        "branches": 40,
        "functions": 50,
        "lines": 50
      
    ,
    "moduleDirectories": [
      "node_modules",
      "app"
    ],
    "moduleNameMapper": 
      ".*\\.(css|less|styl|scss|sass)$": "<rootDir>/internals/mocks/cssModule.js",
      ".*\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/internals/mocks/image.js"
    ,
    "setupTestFrameworkScriptFile": "<rootDir>/internals/testing/test-bundler.js",
    "testRegex": "tests/.*\\.test\\.js$"
  ,
  "lint-staged": 
    "*.js": [
      "npm run lint:eslint:fix",
      "git add --force"
    ],
    "*.json": [
      "prettier --write",
      "git add --force"
    ]
  ,
  "pre-commit": "lint:staged",
  "resolutions": 
    "babel-core": "7.0.0-bridge.0"
  ,
  "dependencies": 
    "@babel/polyfill": "7.0.0",
    "antd": "^3.13.1",
    "axios": "^0.18.0",
    "chalk": "2.4.1",
    "compression": "1.7.3",
    "connected-react-router": "4.5.0",
    "cross-env": "5.2.0",
    "draft-js": "^0.10.5",
    "draft-js-export-html": "^1.3.3",
    "draft-js-export-markdown": "^1.3.3",
    "draft-js-import-html": "^1.3.3",
    "draft-js-import-markdown": "^1.3.3",
    "express": "4.16.4",
    "fontfaceobserver": "2.0.13",
    "history": "4.7.2",
    "hoist-non-react-statics": "3.0.1",
    "immutable": "3.8.2",
    "intl": "1.2.5",
    "invariant": "2.2.4",
    "ip": "1.1.5",
    "loadable-components": "2.2.3",
    "lodash": "4.17.11",
    "minimist": "1.2.0",
    "prop-types": "15.6.2",
    "react": "16.6.0",
    "react-dom": "16.6.0",
    "react-draft-wysiwyg": "^1.13.2",
    "react-flexview": "^4.0.3",
    "react-helmet": "5.2.0",
    "react-intl": "2.7.2",
    "react-logger-lib": "^1.0.5",
    "react-rbac-guard": "0.0.3",
    "react-redux": "5.0.7",
    "react-router-dom": "4.3.1",
    "redux": "4.0.1",
    "redux-immutable": "4.0.0",
    "redux-saga": "0.16.2",
    "reselect": "4.0.0",
    "sanitize.css": "4.1.0",
    "styled-components": "4.0.2",
    "warning": "4.0.2"
  ,
  "devDependencies": 
    "@babel/cli": "7.1.2",
    "@babel/core": "7.1.2",
    "@babel/plugin-proposal-class-properties": "7.1.0",
    "@babel/plugin-syntax-dynamic-import": "7.0.0",
    "@babel/plugin-transform-modules-commonjs": "7.1.0",
    "@babel/plugin-transform-react-constant-elements": "7.0.0",
    "@babel/plugin-transform-react-inline-elements": "7.0.0",
    "@babel/preset-env": "7.1.0",
    "@babel/preset-react": "7.0.0",
    "@babel/register": "7.0.0",
    "add-asset-html-webpack-plugin": "3.1.1",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "10.0.1",
    "babel-loader": "8.0.4",
    "babel-plugin-dynamic-import-node": "2.2.0",
    "babel-plugin-import": "^1.11.0",
    "babel-plugin-lodash": "3.3.4",
    "babel-plugin-react-intl": "3.0.1",
    "babel-plugin-react-transform": "3.0.0",
    "babel-plugin-styled-components": "1.8.0",
    "babel-plugin-transform-react-remove-prop-types": "0.4.19",
    "circular-dependency-plugin": "5.0.2",
    "compare-versions": "3.4.0",
    "compression-webpack-plugin": "2.0.0",
    "coveralls": "3.0.2",
    "css-loader": "1.0.0",
    "enzyme": "3.7.0",
    "enzyme-adapter-react-16": "1.6.0",
    "enzyme-to-json": "3.3.4",
    "eslint": "5.7.0",
    "eslint-config-airbnb": "17.1.0",
    "eslint-config-airbnb-base": "13.1.0",
    "eslint-config-prettier": "3.1.0",
    "eslint-import-resolver-webpack": "0.10.1",
    "eslint-plugin-import": "2.14.0",
    "eslint-plugin-jsx-a11y": "6.1.2",
    "eslint-plugin-prettier": "3.0.0",
    "eslint-plugin-react": "7.11.1",
    "eslint-plugin-redux-saga": "0.9.0",
    "file-loader": "2.0.0",
    "html-loader": "0.5.5",
    "html-webpack-plugin": "3.2.0",
    "image-webpack-loader": "^4.6.0",
    "imports-loader": "0.8.0",
    "jest-cli": "^24.5.0",
    "jest-styled-components": "6.2.2",
    "less": "^3.9.0",
    "less-loader": "^4.1.0",
    "lint-staged": "7.3.0",
    "ngrok": "3.1.0",
    "node-plop": "0.16.0",
    "null-loader": "0.1.1",
    "offline-plugin": "5.0.5",
    "plop": "2.1.0",
    "pre-commit": "1.2.2",
    "prettier": "1.14.3",
    "react-app-polyfill": "0.1.3",
    "react-test-renderer": "16.6.0",
    "rimraf": "2.6.2",
    "shelljs": "0.8.2",
    "style-loader": "0.23.1",
    "stylelint": "^9.10.1",
    "stylelint-config-recommended": "2.1.0",
    "stylelint-config-styled-components": "0.1.1",
    "stylelint-processor-styled-components": "1.5.0",
    "svg-url-loader": "2.3.2",
    "terser-webpack-plugin": "1.1.0",
    "url-loader": "1.1.2",
    "webpack": "4.23.1",
    "webpack-cli": "3.1.2",
    "webpack-dev-middleware": "3.4.0",
    "webpack-hot-middleware": "2.24.3 ",
    "webpack-pwa-manifest": "3.7.1",
    "whatwg-fetch": "3.0.0"
  


【问题讨论】:

【参考方案1】:

您的代码通常看起来是正确的。但正如我所见,您使用了两次convertToRaw

const contentState = editorState.getCurrentContent();
const rawContent = convertToRaw(contentState); // <== convert to raw first time
window.localStorage.setItem(
  "rawContent",
  JSON.stringify(convertToRaw(rawContent)) // <== convert to raw again
);

尝试将JSON.stringify(convertToRaw(rawContent))重写为JSON.stringify(rawContent),我认为它应该可以解决您的问题。

如果这没有帮助,您可以查看这个 jsFiddle,它显示了 Draft.js 如何与本地存储一起使用的常见模式 - https://jsfiddle.net/x2gsp6ju/4/

在这个demo中,你可以看到简单的编辑器组件,当你点击SAVE RAW CONTENT TO LOCAL STORAGE时,我们将当前编辑器内容以字符串的形式保存到本地存储。我们为此使用convertToRawJSON.stringify

 saveRaw = () => 
  var contentRaw = convertToRaw(this.state.editorState.getCurrentContent());

  localStorage.setItem('draftRaw', JSON.stringify(contentRaw));

如果之后您重新加载页面,您的编辑器将使用您保存的内容和样式进行初始化。因为在constructor 中,我们读取了适当的本地存储属性,并通过JSON.parseconvertFromRawcreateWithContent 方法使用先前存储的内容初始化编辑器。

constructor(props) 
  super(props);

  let initialEditorState = null;
  const storeRaw = localStorage.getItem('draftRaw');

  if (storeRaw) 
    const rawContentFromStore = convertFromRaw(JSON.parse(storeRaw));
    initialEditorState = EditorState.createWithContent(rawContentFromStore);
   else 
    initialEditorState = EditorState.createEmpty();
  

  this.state = 
    editorState: initialEditorState
  ;

【讨论】:

我感到很尴尬,我没有发现我两次转换为 raw 的事实。这正是我的代码中的问题。非常感谢。

以上是关于如何从 localStorage 读取 DraftJS 状态?的主要内容,如果未能解决你的问题,请参考以下文章

Chrome 中的 iframe 错误:无法从“窗口”读取“localStorage”:此文档的访问被拒绝

iframe chrome:未捕获的 DOMException:无法从“Window”读取“localStorage”属性:此文档的访问被拒绝

尝试读取 localStorage 时出现登录错误

localStorage用法小总结

localStorage和sessionStorage学习小结

如何从我的组件打开的窗口中反应性地检查更改