我在这个问题中有一个运行示例,可以找到搜索输入,文件树和文本框。
目前我正在寻找一个解决方案,其中从'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);
}
}
答案 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>