React - 单击组件​​内的单击文本,将显示图像

时间:2018-05-06 17:56:43

标签: javascript reactjs

我在这个问题中有一个运行示例,可以找到搜索输入文件树文本框

目前我正在寻找一个解决方案,其中'README.md' 选择一个节点(例如<FileTree />)之后,显示名称 inside { {1}}(类似于:<TextBox />)组件

然后,用户可以点击该名称(在这种情况下,它将是'./README.md'),并且图片会显示在名为 './README.md' (图片不应该是可以删除的)

我了解点击事件需要在<DetailBox />内定义,并将状态设置为<TextBox />

所以,我很困惑我应该如何传递道具和初始状态,因为图片是外部链接?它必须是外部链接。

感谢您提供的帮助。

<DetailBox />
// TEXT BOX COMPONENT
class TextBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      content: "Select A Node To See Its Data Structure 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 {
  constructor(props) {
    super(props);
    this.state = { image: '' }
  }

  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"));
* {
  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);
  }
}

1 个答案:

答案 0 :(得分:1)

也许我误解了这个问题,但我认为您只是想将图片网址作为道具传递给DetailBox,而不是试图在DetailBox内维持状态。

我认为保持表现性成分愚蠢并让决定来自大脑&#34;或经理或父母组成部分。

试试这个:

class DetailBox extends React.Component {
  render() {
    return (
      <div>
        <img alt="" src={this.props.image} /> 
      </div>
    );
  }
}

如果您有时只想显示DetailBox,那么在App中,您需要有条件地呈现它,如下所示:

    <div className="col-md-6">
      { this.state.showDetailBox ? <DetailBox image={this.props.image} /> : null }
    </div>