使用React美化JSON

197

我正在使用ReactJS,我的应用程序的一部分需要漂亮地打印JSON。

我得到一些JSON,例如:{ "foo": 1, "bar": 2 },如果我在浏览器控制台中运行JSON.stringify(obj, null, 4),它会打印漂亮的格式,但是当我将其用于这个React代码片段时:

render: function() {
  var json = this.getStateFromFlux().json;
  return (
    <div>
      <JsonSubmitter onSubmit={this.onSubmit} />
      { JSON.stringify(json, null, 2) }
    </div>
  );
},

它呈现出像"{ \"foo\" : 2, \"bar\": 2}\n"这样的丑陋JSON。

我该如何使这些字符被正确解释?{


13
你尝试过使用 JSON.stringify(json, null, "\t") 吗? - brroshan
结果发现我犯了一个愚蠢的错误,即 this.getStateFromFlux().json 已经返回了一个字符串。我将其修改为保存JS对象,现在它完美无缺地工作了。 - Brandon
2
另请参见 https://github.com/alexkuz/react-json-tree - Aprillion
7个回答

352

您需要在生成的字符串中适当地插入BR标签,或者使用例如PRE标签,以保留stringify的格式:

var data = { a: 1, b: 2 };

var Hello = React.createClass({
    render: function() {
        return <div><pre>{JSON.stringify(data, null, 2) }</pre></div>;
    }
});

React.render(<Hello />, document.getElementById('container'));

演示链接

更新

class PrettyPrintJson extends React.Component {
    render() {
         // data could be a prop for example
         // const { data } = this.props;
         return (<div><pre>{JSON.stringify(data, null, 2) }</pre></div>);
    }
}

ReactDOM.render(<PrettyPrintJson/>, document.getElementById('container'));

示例

无状态函数组件,React .14 或更高版本

const PrettyPrintJson = ({data}) => {
    // (destructured) data could be a prop for example
    return (<div><pre>{ JSON.stringify(data, null, 2) }</pre></div>);
}

或者,...

const PrettyPrintJson = ({data}) => (<div><pre>{ 
    JSON.stringify(data, null, 2) }</pre></div>);

工作示例

备忘录 / 16.6+

(你甚至可能想使用一个备忘录, 16.6+)

const PrettyPrintJson = React.memo(({data}) => (<div><pre>{
    JSON.stringify(data, null, 2) }</pre></div>));

4
谢谢!我不知道可选的JSON.stringify参数。Javascript太棒了^^ - Marcel Ennix
1
这很完美 - 最简单的解决方案总是最好的!我建议添加 highlight.js 来实现一些语法高亮和主题外观。 - KeepingItClassy
这很美。 - JChao
<pre>标签解决方案完美运行,这是正确的方法! - Vikram K
1
太好了,正是我想要的 :) - Juangui Jordán

49

简述

如何在JavaScript和React中漂亮地打印JSON

<pre>{JSON.stringify(data, null, 2)}</pre>

4
简单就是关键。谢谢! - Marinus Klasen
如果您的数据包含BigInt,则无法正常工作。 - o-az

31

仅是对WiredPrairie's答案的一点扩展,这是一个可以打开和关闭的小组件。

可以像这样使用:

<Pretty data={this.state.data}/>

在此输入图片描述

export default React.createClass({

    style: {
        backgroundColor: '#1f4662',
        color: '#fff',
        fontSize: '12px',
    },

    headerStyle: {
        background-color: '#193549',
        padding: '5px 10px',
        fontFamily: 'monospace',
        color: '#ffc600',
    },

    preStyle: {
        display: 'block',
        padding: '10px 30px',
        margin: '0',
        overflow: 'scroll',
    },

    getInitialState() {
        return {
            show: true,
        };
    },

    toggle() {
        this.setState({
            show: !this.state.show,
        });
    },

    render() {
        return (
            <div style={this.style}>
                <div style={this.headerStyle} onClick={ this.toggle }>
                    <strong>Pretty Debug</strong>
                </div>
                {( this.state.show ?
                    <pre style={this.preStyle}>
                        {JSON.stringify(this.props.data, null, 2) }
                    </pre> : false )}
            </div>
        );
    }
});

更新

采用更现代的方法(现在createClass正在退出)

import styles from './DebugPrint.css'

import autoBind from 'react-autobind'
import classNames from 'classnames'
import React from 'react'

export default class DebugPrint extends React.PureComponent {
  constructor(props) {
    super(props)
    autoBind(this)
    this.state = {
      show: false,
    }
  }    

  toggle() {
    this.setState({
      show: !this.state.show,
    });
  }

  render() {
    return (
      <div style={styles.root}>
        <div style={styles.header} onClick={this.toggle}>
          <strong>Debug</strong>
        </div>
        {this.state.show 
          ? (
            <pre style={styles.pre}>
              {JSON.stringify(this.props.data, null, 2) }
            </pre>
          )
          : null
        }
      </div>
    )
  }
}

还有您的样式文件

.root {
    background-color: #1f4662;
    color: #fff;
    font-size: 12px;
}

.header {
    background-color: #193549;
    padding: 5px 10px;
    font-family: monospace;
    color: #ffc600;
}

.pre {
    display: block;
    padding: 10px 30px;
    margin: 0;
    overflow: scroll;
}

2
转换为组件:https://toddmotto.com/react-create-class-versus-component/ - whitneyland
我喜欢组件类中样式是另一个关键因素的方式。 - pery mimon
顺便问一下,在 CSS 文件中,你没有忘记删除值周围的撇号 '' 吗? - pery mimon
@perymimon 很好的观点!已更新。还需要为功能组件进行更新。 - Chris
@Chris 在我运行它之后,我注意到你应该将 CSS 的驼峰命名法转换为短横线命名法。但除此之外,它运行得非常好。干得好! - pery mimon
显示剩余2条评论

18

'react-json-view' 提供了显示 json 字符串的解决方案。

import ReactJson from 'react-json-view';
<ReactJson src={my_important_json} theme="monokai" />

10
const getJsonIndented = (obj) => JSON.stringify(newObj, null, 4).replace(/["{[,\}\]]/g, "")

const JSONDisplayer = ({children}) => (
    <div>
        <pre>{getJsonIndented(children)}</pre>
    </div>
)

那么你就可以轻松使用它:

const Demo = (props) => {
   ....
   return <JSONDisplayer>{someObj}<JSONDisplayer>
}

2
翻译:简短明了
<div>
  <pre dangerouslySetInnerHTML={{
     __html: JSON.stringify(data, null, 2),
  }} />
</div>

⚠️ 提醒:dangerouslySetInnerHTML 存在 XSS 漏洞。如果您选择使用这种方法,请务必对 data 内容进行清理处理。 - undefined

0
这是一个基于Chris的回答,使用React Hooks编写的演示react_hooks_debug_print.html。JSON数据示例来自https://json.org/example.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

    <!-- Don't use this in production: -->
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script src="https://raw.githubusercontent.com/cassiozen/React-autobind/master/src/autoBind.js"></script>

    <script type="text/babel">

let styles = {
  root: { backgroundColor: '#1f4662', color: '#fff', fontSize: '12px', },
  header: { backgroundColor: '#193549', padding: '5px 10px', fontFamily: 'monospace', color: '#ffc600', },
  pre: { display: 'block', padding: '10px 30px', margin: '0', overflow: 'scroll', }
}

let data = {
  "glossary": {
    "title": "example glossary",
    "GlossDiv": {
      "title": "S",
      "GlossList": {
        "GlossEntry": {
          "ID": "SGML",
          "SortAs": "SGML",
          "GlossTerm": "Standard Generalized Markup Language",
          "Acronym": "SGML",
          "Abbrev": "ISO 8879:1986",
          "GlossDef": {
            "para": "A meta-markup language, used to create markup languages such as DocBook.",
            "GlossSeeAlso": [
              "GML",
              "XML"
            ]
          },
          "GlossSee": "markup"
        }
      }
    }
  }
}

const DebugPrint = () => {
  const [show, setShow] = React.useState(false);

  return (
    <div key={1} style={styles.root}>
    <div style={styles.header} onClick={ ()=>{setShow(!show)} }>
        <strong>Debug</strong>
    </div>
    { show 
      ? (
      <pre style={styles.pre}>
       {JSON.stringify(data, null, 2) }
      </pre>
      )
      : null
    }
    </div>
  )
}

ReactDOM.render(
  <DebugPrint data={data} />, 
  document.getElementById('root')
);

    </script>

  </body>
</html>

或者以以下方式将样式添加到标题中:

    <style>
.root { background-color: #1f4662; color: #fff; fontSize: 12px; }
.header { background-color: #193549; padding: 5px 10px; fontFamily: monospace; color: #ffc600; }
.pre { display: block; padding: 10px 30px; margin: 0; overflow: scroll; }
    </style>

DebugPrint替换为以下内容:

const DebugPrint = () => {
  // https://dev59.com/_l0a5IYBdhLWcg3wD08e
  const [show, setShow] = React.useState(false);

  return (
    <div key={1} className='root'>
    <div className='header' onClick={ ()=>{setShow(!show)} }>
        <strong>Debug</strong>
    </div>
    { show 
      ? (
      <pre className='pre'>
       {JSON.stringify(data, null, 2) }
      </pre>
      )
      : null
    }
    </div>
  )
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接