React - 如何将道具图像传递给文本组件

时间:2018-05-07 10:03:21

标签: javascript reactjs

我开发了一个小应用程序,用户可以在其中找到搜索输入,文件树和文本框。你可以在这个问题中找到一个例子。

基本上,它应该像这样工作: 点击<FileTree /> =&gt;中的README.md ./README.md =&gt;内显示<TextBox />点击./README.md =&gt; 出现Morpheus图像。 正如您所看到的,图像未与<TextBox />内的点击相关联,因此无法满足理想预期。

这让我感到困惑。

我知道点击事件需要在<TextBox />内定义,并将道具传递给负责图像的组件。 但是,我很困惑,我应该如何传递道具和&#34;存储&#34;网址,因为图片是外部链接,对吗?

此外,只需要一张图像,多张图片。

有人可以帮忙吗? 我是ReactJS的新手。有些事情对我来说有点混乱和新鲜。提前感谢您提供的帮助。

&#13;
&#13;
// TEXT BOX COMPONENT
class TextBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      content: "Select A Node To See Its Name Here..."
    };
    this.changeContent = this.changeContent.bind(this);
  }

  changeContent(newContent) {
    this.setState({
      content: newContent
    });
  }
  
   imageClick(newImage) {
    console.log("TEXT INSIDE <TextBox /> WAS CLICKED");
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      content: nextProps.content
    });
  }

  render() {
    return (
      <div className="padd_top">
        <div className="content_box">{this.state.content}</div>
      </div>
    );
  }
}

// SEARCH COMPONENT
class SearchEngine extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ""
    };
    this.inputChange = this.inputChange.bind(this);
  }

  inputChange(e) {
    const content = e.target.value;
    this.props.onChange(content);
  }

  render() {
    return (
      <input className="form-control" placeholder="Search the tree..." onChange={this.inputChange} />
    );
  }
}

// IMAGE COMPONENT
class DetailBox extends React.Component {
  render() {
    return (
      <div>
        <img alt="" src="https://media.makeameme.org/created/what-if-I-y0ivox.jpg" /> 
      </div>
    );
  }
}

// FILE TREE COMPONENT
let data = [
  {
    type: "directory",
    name: ".",
    contents: [
      {
        type: "directory",
        name: "./bin",
        contents: [{ type: "file", name: "./bin/greet" }]
      },
      {
        type: "directory",
        name: "./lib",
        contents: [{ type: "file", name: "./lib/greeting.rb" }]
      },
      {
        type: "directory",
        name: "./spec",
        contents: [
          { type: "file", name: "./spec/01_greeting_spec.rb" },
          { type: "file", name: "./spec/02_cli_spec.rb" },
          { type: "file", name: "./spec/spec_helper.rb" }
        ]
      },
      { type: "file", name: "./CONTRIBUTING.md" },
      { type: "file", name: "./Gemfile" },
      { type: "file", name: "./Gemfile.lock" },
      { type: "file", name: "./LICENSE.md" },
      { type: "file", name: "./README.md" }
    ]
  }
];

// Icon file image for 'FileTree'
const FileIcon = () => {
  return (
    <div className="svg-icon">
      <svg
        id="icon-file-text2"
        className="icon"
        viewBox="0 0 32 32"
        fill="currentColor"
        width="1em"
        height="1em"
      >
        <path d="M28.681 7.159c-0.694-0.947-1.662-2.053-2.724-3.116s-2.169-2.030-3.116-2.724c-1.612-1.182-2.393-1.319-2.841-1.319h-15.5c-1.378 0-2.5 1.121-2.5 2.5v27c0 1.378 1.122 2.5 2.5 2.5h23c1.378 0 2.5-1.122 2.5-2.5v-19.5c0-0.448-0.137-1.23-1.319-2.841zM24.543 5.457c0.959 0.959 1.712 1.825 2.268 2.543h-4.811v-4.811c0.718 0.556 1.584 1.309 2.543 2.268zM28 29.5c0 0.271-0.229 0.5-0.5 0.5h-23c-0.271 0-0.5-0.229-0.5-0.5v-27c0-0.271 0.229-0.5 0.5-0.5 0 0 15.499-0 15.5 0v7c0 0.552 0.448 1 1 1h7v19.5z" />
        <path d="M23 26h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z" />
        <path d="M23 22h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z" />
        <path d="M23 18h-14c-0.552 0-1-0.448-1-1s0.448-1 1-1h14c0.552 0 1 0.448 1 1s-0.448 1-1 1z" />
      </svg>
    </div>
  );
};

// Icon folder image for 'FileTree'
 const FolderIcon = () => {
  return (
    <div className="svg-icon">
      <svg
        id="icon-folder"
        className="icon"
        viewBox="0 0 32 32"
        fill="currentColor"
        height="1em"
        width="1em"
      >
        <path d="M14 4l4 4h14v22h-32v-26z" />
      </svg>
    </div>
  );
};

// Icon arrow image for 'FileTree'
 const TriangleDown = () => {
  return (
    <div className="svg-icon">
      <svg
        id="svg__icon--triangle-down"
        viewBox="0 0 9 4.5"
        fill="currentColor"
        height="1em"
        width="1em"
      >
        <path d="M0,0,4.5,4.5,9,0Z" />
      </svg>
    </div>
  );
};

// Filters file 'name' and adds '/'
const formatName = name => {
  return name.substr(name.lastIndexOf("/") + 1);
};

// Dummy data set
var root = data[0];

// Construction of FileTree
 class FileTree extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeNode: null
    };
    this.setActiveNode = this.setActiveNode.bind(this);
  }

  componentWillReceiveProps({ searchTerm }) {
    this.setState({ searchTerm });
  }

  setActiveNode(name) {
    this.setState({ activeNode: name });
    this.props.liftStateUp(name);
  }

  render() {
    return (
      <div className="padd_top">
        {renderTree(
          this.props.root || root,
          this.setActiveNode,
          this.state.activeNode,
          null,
          this.state.searchTerm
        )}
      </div>
    );
  }
}

class Directory extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      expanded: true,
    };
    this.toggleDirectory = this.toggleDirectory.bind(this);
  }

  toggleDirectory() {
    this.setState({ expanded: !this.state.expanded });
  }

  hasMatchingNodes() {
    const filteredNodes = this.props.node.contents.filter(
      (node) => {
      return (
        node.name.toLowerCase().indexOf(this.props.searchTerm.toLowerCase()) >
        -1
      );
    });

    console.log(this.props.searchTerm);
    return filteredNodes.length > 0;
  }
  render() {
    let node = this.props.node;

    if (this.props.searchTerm && !this.hasMatchingNodes()) return null;
    return (
      <div className="directory-container">
        <div className="directory">
          <div
            className=
            {`directory__toggle ${ this.state.expanded ? "expanded" : ""}`}
          >
            <div onClick={this.toggleDirectory}>
              <TriangleDown />
            </div>
          </div>

          <div className="directory__icon" onClick={this.toggleDirectory}>
            <FolderIcon />
          </div>

          <div className="directory__name" onClick={this.toggleDirectory}>
            <div>{formatName(node.name)}</div>
          </div>
        </div>
        {this.state.expanded
          ? node.contents.map((content, index) =>
              renderTree(
                content,
                this.props.setActiveNode,
                this.props.activeNode,
                index,
                this.props.searchTerm
              )
            )
          : ""}
      </div>
    );
  }
}

// Set class Active do selected file
const File = ({ name, setActiveNode, activeNode, searchTerm }) => {
  if (searchTerm && name.toLowerCase().indexOf(searchTerm.toLowerCase()) < 0)
    return null;
  let isActive = activeNode === name;
  let className = isActive ? "active" : "";

  return (
    <div className={className + " file"} onClick={() => setActiveNode(name)}>
      <div className="file__icon">
        <FileIcon />
      </div>
      <div className="file__name">{formatName(name)}</div>
      {isActive && <div className="file__options">...</div>}
    </div>
  );
};

var renderTree = (node, setActiveNode, activeNode, index, searchTerm) => {
  if (node.type === "file") {
    return (
      <File
        key={index}
        name={node.name}
        setActiveNode={setActiveNode}
        activeNode={activeNode}
        searchTerm={searchTerm}
      />
    );
  } else if (node.type === "directory") {
    return (
      <Directory
        key={index}
        node={node}
        setActiveNode={setActiveNode}
        activeNode={activeNode}
        searchTerm={searchTerm}
      />
    );
  } else {
    return null;
  }
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeNode: ""
    };

    this.onChange = this.onChange.bind(this);
  }

  liftStateUp = (data) => {
    this.setState({ activeNode: data });
  };

  onChange(data) {
    this.setState({ searchTerm: data });
  }
  render() {
    return (
      <div>
        <div className="col-md-12">
          <SearchEngine className="form-control" onChange={this.onChange} />
        </div>
        <div className="col-md-6">
          <FileTree
            liftStateUp={this.liftStateUp}
            searchTerm={this.state.searchTerm}
          />
        </div>
        <div className="col-md-6">
          <TextBox
            content={this.state.activeNode}
            onClick={this.onClickImage}
          />
        </div>
        <div className="col-md-6">
          <DetailBox image={this.props.image} />
        </div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("app"));
&#13;
* {
  font-family: Helvetica;
  background-color: #212830;
  color: #9ea1b2 !important;
}

.directory {
  padding-left: 10px;
  padding-top: 1px;
  padding-bottom: 1px;
  display: flex;
  flex-direction: row;
  align-items: center;
}

.directory__toggle {
  padding-left: 10px;
  transform: rotate(-90deg);
}

.directory__icon {
  padding-left: 10px;
}

.directory__icon {
  padding-left: 10px;
}

.directory__name {
  padding-left: 10px;
}

.directory-container {
  padding-left: 10px;
}

.file {
  padding-left: 50px;
  padding-top: 1px;
  padding-bottom: 1px;
  display: flex;
}

.file__icon {
  padding-left: 10px;
}

.file__name {
  padding-left: 10px;
}

.file__options {
  align-self: flex-end;
}

.active {
  color: #ffffff;
  background-color: #31343f;
}

.icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  stroke-width: 0;
  stroke: currentColor;
  fill: currentColor;
}

.svg-icon {
  width: 1em;
  height: 1em;
}

.expanded {
  transform: rotate(0deg);
}

/** CONTENT BOX **/

.padd_top {
  padding-top: 20px;
}

.content_box {
  font-size: 12px;
  white-space: pre-wrap;
  background-color: #282c34;
  border: solid 1px black;
  padding: 20px;
  color: #9da5ab;
  min-height: 250px;
  margin-bottom: 15px;
}

.text_color {
  color: #21252b !important;
}

/** SEARCH **/

.form-control {
  margin-top: 15px;
}

/** ARROW ANIMATION **/

.rotate {
  animation: rotate-keyframes 1s;
}

@keyframes rotate-keyframes {
  from {
    transform: rotate(-90deg);
  }

  to {
    transform: rotate(0deg);
  }
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<!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>
	<div id="app"></div>
</body>

</html>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

仔细分析后,我发现您的代码缺少一些工作原因:

首先,在<TextBox/>内你需要正确定义onClick事件,将src作为道具传递给<DetailBox/>,如下所示:

<div onClick={() => this.props.onClick()} className="content_box">
  {this.state.content}
</div>

然后,在您的父组件<App/>中,您可以定义一个州。我使用名称visible定义了图像的状态,然后使用名为showImage()的方法定义了图像的状态,以便在单击时更改状态。

由于您不希望图片具有“可嵌入性”,因此当您在true内定义this.setState() { visible: true }时,状态仅会更改为showImage()。这样,每次点击都不会改变状态。

constructor(props) {
    super(props);
    this.state = {
      activeNode: "",
      visible: false
    };
    this.onChange = this.onChange.bind(this);
    this.showImage = this.showImage.bind(this);
  }

showImage() {
  this.setState({ visible: true });
}

最后,不要忘记将onClick={this.showImage}添加到<TextBox/>组件和{ visible && <DetailBox src={this.props.src} /> },以便连接点击事件,从<TextBox/>传递道具,连接{ {1}} src更改图片的状态。像这样:

<DetailBox/>

我希望这有帮助!