代码之家  ›  专栏  ›  技术社区  ›  Wonka

React-如何呈现嵌套组件而不是[object]?

  •  1
  • Wonka  · 技术社区  · 7 年前

    Accordion , AccordionItem ,和 Link . 一切都很好除了 链接 ,它在小提琴中的手风琴元件线50之外时呈现完美,但在嵌套中时不起作用 sampleQuestions > question1 > answer ,小提琴第59行。

    答案如下: Ottawa baby!! Check [object Object] for more details.

    Ottawa baby!! Check wikipedia link b for more details.

    下面是供参考的代码,但我建议直接跳到代码下面的小提琴上,单击第一个问题,直接查看问题。

    class Link extends React.Component {
      render() {
        return (
            <span onClick={this.props.onClick} className="link">{this.props.linkTitle}</span>
        );
      }
    }
    
    class AccordionItem extends React.Component {
      constructor() {
        super();
        this.state = {
          active: false
        };
        this.toggle = this.toggle.bind(this);
      }
      toggle() {
        this.setState({
          active: !this.state.active,
          className: "active"
        });
      }
      render() {
        const activeClass = this.state.active ? "active" : "inactive";
        const question = this.props.details;
        return (
                <div className={activeClass} onClick={this.toggle}>
                  <span className="summary">{question.summary}</span>
                  <span className="folding-pannel answer">{question.answer}</span>
                </div>
        );
      }
    }
    
    class Accordion extends React.Component {
      constructor() {
        super();
        this.state = {
          questions: sampleQuestions,
        };
        this.renderQuestion = this.renderQuestion.bind(this);
      }
      renderQuestion(key) {
        return <AccordionItem key={key} index={key} details={this.state.questions[key]} />
      }
      render() {
        return(
          <div className="mainbody">
            <h1>What is...</h1>
            <Link onClick={() => alert('outside link works')} linkTitle={'wikipedia link a'} />
            <div className="accordion-container">
              {Object.keys(this.state.questions).map(this.renderQuestion)}
            </div>
          </div>    
        )
      }
    }
    const sampleQuestions = {
      question1: {summary:'the capital of Canada?', answer:'Ottawa baby!! Check ' + <Link onClick={() => alert('trying to get this nested link to show')} linkTitle={'wikipedia link b'} /> + ' for more details.'},
      question2: {summary:'the life span of a bowhead whale?', answer:'Over 200 years!!'},
      question3: {summary:'the most visited city in the world?', answer:'London, groovy baby!!'},
      question4: {summary:'the warmest ocean?', answer:'Indian Ocean, it\'s a hottie!'},
      question5: {summary:'the one thing ron swanson hates more than lying?', answer:'Skim milk, which is water that\'s lying about being milk'}
    };
    ReactDOM.render(
      <Accordion />,
      document.getElementById('accordion')
    );
    

    Here is the fiddle

    [object Object] 渲染所需的 第一个问题答案的组成部分?

    3 回复  |  直到 7 年前
        1
  •  1
  •   Community Mohan Dere    6 年前

    这里有一种方法:利用React片段。

    工作示例: https://codesandbox.io/s/92r12m7zp

    公开/index.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta name="theme-color" content="#000000">
        <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
        <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
        <title>React App</title>
    </head>
    
    <body>
        <noscript>
            You need to enable JavaScript to run this app.
        </noscript>
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
            <pattern id="pattern" x="0" y="0" width="24" height="24" patternUnits="userSpaceOnUse">
                <rect fill="rgba(159, 188, 191, 0.15)" x="0" width="20" height="20" y="0" />
                <rect fill="rgba(159, 188, 191, 0.15)" x="20" width="20" height="20" y="20" />
            </pattern>
            <rect fill="url(#pattern)" x="0" y="0" width="100%" height="100%" />
        </svg>
        <div id="accordion"></div>
    </body>
    
    </html>
    

    import React from "react";
    import { render } from "react-dom";
    import Accordian from "./Accordian";
    import "./styles.css";
    
    render(<Accordian />, document.getElementById("accordion"));
    

    Accordian.js

    import map from "lodash/map";
    import React, { Component } from "react";
    import AccordionItem from "./AccordianItem";
    import SampleQuestions from "./sampleQuestions";
    
    export default class Accordion extends Component {
      state = { questions: SampleQuestions };
    
      render = () => (
        <div className="mainbody">
          <h1>What is...</h1>
          <div className="accordion-container">
            {map(this.state.questions, ({ key, ...rest }) => (
              <AccordionItem key={key} {...rest} />
            ))}
          </div>
        </div>
      );
    }
    

    AccordianItem.js

    import React, { Component } from "react";
    
    export default class AccordionItem extends Component {
      state = { isActive: false };
    
      toggle = () => this.setState(prevState => ({ isActive: !this.state.isActive }));
    
      render = () => (
        <div
          className={`${this.state.isActive ? "active" : "inactive"}`}
          onClick={this.toggle}
        >
          <span className="summary">&#62; {this.props.summary}</span>
          <span className="folding-pannel answer">
            {this.props.answer}
          </span>
        </div>
      );
    }
    

    sampleQuestions.js

    import React, { Fragment } from "react";
    
    const Link = url => (
      <a href={url} target="_blank">
        here
      </a>
    );
    
    export default [
      {
        key: "capital-of-canada",
        summary: "the capital of Canada?",
        answer: (
          <Fragment>
            Ottawa baby!! Click {Link("https://en.wikipedia.org/wiki/Ottawa")} for
            more details
          </Fragment>
        )
      },
      {
        key: "whale-lifespan",
        summary: "the life span of a bowhead whale?",
        answer: "Over 200 years!!"
      },
      {
        key: "most-popular-city",
        summary: "the most visited city in the world?",
        answer: "London, groovy baby!!"
      },
      {
        key: "warmest-ocean",
        summary: "the warmest ocean?",
        answer: "Indian Ocean, it's a hottie!"
      },
      {
        key: "swanson",
        summary: "the one thing ron swanson hates more than lying?",
        answer: "Skim milk, which is water that's lying about being milk"
      }
    ];
    

    还有另一种方法:使用混合内容的数组。

    https://codesandbox.io/s/1v1xmq1kmq

    公开/index.html

    <!DOCTYPE html>
    <html lang=“en”>
    
    <meta charset=“utf-8”>
    <meta name=“viewport”content=“width=device width,initial scale=1,shrink to fit=no”>
    <meta name=“主题颜色”content=“#000000”>
    <link rel=“manifest”href=“%PUBLIC\u URL%/manifest.json“>
    <link rel=“shortcut icon”href=“%PUBLIC\u URL%/favicon.ico“>
    <标题>反应应用程序</标题>
    </头部>
    
    <车身>
    <noscript>
    您需要启用JavaScript才能运行此应用程序。
    </noscript>
    <svg xmlns=“http://www.w3.org/2000/svg“version=”1.1“>
    <pattern id=“pattern”x=“0”y=“0”width=“24”height=“24”patternUnits=“userSpaceOnUse”>
    <rect fill=“rgba(159、188、191、0.15)”x=“0”width=“20”height=“20”y=“0”/>
    <rect fill=“rgba(159、188、191、0.15)”x=“20”width=“20”height=“20”y=“20”/>
    </图案>
    <rect fill=“url(#pattern)”x=“0”y=“0”width=“100%”height=“100%”/>
    </svg>
    <div id=“手风琴”></部门>
    
    </html>
    

    index.js

    从“React”导入React;
    从“/accorbian”导入accorbian;
    导入”/styles.css";
    
    渲染(<手风琴式/>,document.getElementById(“手风琴”);
    

    Accordian.js

    从“lodash/map”导入地图;
    从“React”导入React,{Component};
    
    导出默认类Accordion扩展组件{
    state={questions:SampleQuestions};
    
    <h1>什么是…</h1>
    <div className=“手风琴容器”>
    {地图(this.state.questions,({key,…rest})=>(
    <accordioItem key={key}{…rest}/>
    ))}
    </部门>
    </部门>
    );
    

    AccordianItem.js

    import each from "lodash/each";
    import React, { Component, Fragment } from "react";
    import uuid from "uuid/v5";
    
    export default class AccordionItem extends Component {
      state = { isActive: false };
    
      toggle = () => this.setState(prevState => ({ isActive: !this.state.isActive }));
    
      render = () => (
        <div
          className={`${this.state.isActive ? "active" : "inactive"}`}
          onClick={this.toggle}
        >
          <span className="summary">&#62; {this.props.summary}</span>
          <span className="folding-pannel answer">
            {each(this.props.answer, prop => <Fragment key={uuid}>{prop}</Fragment>)}
          </span>
        </div>
      );
    }
    

    sampleQuestions.js

    import React from "react";
    
    const Link = url => (
      <a href={url} target="_blank">
        here
      </a>
    );
    
    export default [
      {
        key: "capital-of-canada",
        summary: "the capital of Canada?",
        answer: [
          "Ottawa baby!! Click ",
          Link("https://en.wikipedia.org/wiki/Ottawa"),
          " for more details"
        ]
      },
      {
        key: "whale-lifespan",
        summary: "the life span of a bowhead whale?",
        answer: ["Over 200 years!!"]
      },
      {
        key: "most-popular-city",
        summary: "the most visited city in the world?",
        answer: ["London, groovy baby!!"]
      },
      {
        key: "warmest-ocean",
        summary: "the warmest ocean?",
        answer: ["Indian Ocean, it's a hottie!"]
      },
      {
        key: "swanson",
        summary: "the one thing ron swanson hates more than lying?",
        answer: ["Skim milk, which is water that's lying about being milk"]
      }
    ];
    

    还有另一种方法:利用 dangerouslySetInnerHTML sanitize-html

    工作示例: https://codesandbox.io/s/0q1mv0omkw

    公开/index.html

    <!DOCTYPE html>
    <html lang=“en”>
    
    <头部>
    <meta name=“viewport”content=“width=device width,initial scale=1,shrink to fit=no”>
    <meta name=“主题颜色”content=“#000000”>
    <link rel=“manifest”href=“%PUBLIC\u URL%/manifest.json“>
    <link rel=“shortcut icon”href=“%PUBLIC\u URL%/favicon.ico“>
    <标题>反应应用程序</标题>
    
    <noscript>
    您需要启用JavaScript才能运行此应用程序。
    </noscript>
    <pattern id=“pattern”x=“0”y=“0”width=“24”height=“24”patternUnits=“userSpaceOnUse”>
    <rect fill=“rgba(159、188、191、0.15)”x=“20”width=“20”height=“20”y=“20”/>
    </图案>
    <rect fill=“url(#pattern)”x=“0”y=“0”width=“100%”height=“100%”/>
    </svg>
    <div id=“手风琴”></部门>
    </车身>
    
    

    从“React”导入React;
    从“react dom”导入{render};
    导入”/styles.css";
    
    渲染(<手风琴式/>,document.getElementById(“手风琴”);
    

    Accordian.js

    从“lodash/map”导入地图;
    从“/AccordianItem”导入AccordianItem;
    从“/SampleQuestions”导入SampleQuestions;
    
    state={questions:SampleQuestions};
    
    渲染=()=>(
    <div className=“主体”>
    <div className=“手风琴容器”>
    {地图(this.state.questions,({key,…rest})=>(
    ))}
    </部门>
    </部门>
    );
    

    AccordianItem.js

    import React, { Component } from "react";
    import sanitizeHtml from "sanitize-html";
    
    export default class AccordionItem extends Component {
      state = { isActive: false };
    
      toggle = () => this.setState(prevState => ({ isActive: !this.state.isActive }));
    
      sanitize = ans =>
        sanitizeHtml(ans, {
          allowedTags: ["a"],
          allowedAttributes: {
            a: ["href", "target"]
          }
        });
    
      render = () => (
        <div
          className={`${this.state.isActive ? "active" : "inactive"}`}
          onClick={this.toggle}
        >
          <span className="summary">&#62; {this.props.summary}</span>
          <span
            className="folding-pannel answer"
            dangerouslySetInnerHTML={{
              __html: this.sanitize(this.props.answer)
            }}
          />
        </div>
      );
    }
    

    const Link = url => `<a href=${url} target="_blank">here</a>`;
    
    export default [
      {
        key: "capital-of-canada",
        summary: "the capital of Canada?",
        answer: `Ottawa baby!! Click ${Link("https://en.wikipedia.org/wiki/Ottawa")} for more details`
      },
      {
        key: "whale-lifespan",
        summary: "the life span of a bowhead whale?",
        answer: "Over 200 years!!"
      },
      {
        key: "most-popular-city",
        summary: "the most visited city in the world?",
        answer: "London, groovy baby!!"
      },
      {
        key: "warmest-ocean",
        summary: "the warmest ocean?",
        answer: "Indian Ocean, it's a hottie!"
      },
      {
        key: "swanson",
        summary: "the one thing ron swanson hates more than lying?",
        answer: "Skim milk, which is water that's lying about being milk"
      }
    ];
    
        2
  •  1
  •   Wonka    7 年前

    谢谢所有回答的人,但我找到了一个不那么突兀的方法。正如patrick在评论中所建议的,简单地将答案转换为jsx而不是字符串,通过将其包装到div中就可以完美地工作。

    answer: <div>Ottawa baby!! Check <Link onClick={() => alert('trying to get this nested link to show')} linkTitle={'wikipedia link b'} /> for more details.</div>
    

        3
  •  0
  •   Bernardo Siqueira    7 年前

    通常不能将react组件放在字符串中。有很多方法可以做到这一点,即使用 react-jsx-parser ,但我们不谈这个。

    一个可能的解决方案是执行以下操作:设置一个哑组件来呈现子数组。

    const AnswerWithLink = (children) => {
      return (
        <span>{[...children]}</span>
      )
    }
    

    然后在回答1时,将其作为函数调用,并将字符串的各个部分作为数组的元素传递:

    question1: {
      summary:'the capital of Canada?', 
      answer: AnswerWithLink(['Ottawa baby!! Check ', Link({onClick: () => alert('trying to get this nested link to show'), linkTitle: 'wikipedia link b'}), ' for more details'])
    }
    

    不过,我相信还有更有效的方法。

    编辑:我也编辑了代码笔使其工作。最后也改变了 <Link />