好!我在这里列出了这个问题的更简洁示例:Need Help Creating a Simple List Maker App in React JS
我正在研究React,并为我的投资组合构建一个简单的列表制作器应用程序。但是我卡住了!我正在尝试为输入表单和按钮创建功能,当单击它们时,会将信息添加到页面正文中的列表中。当我学习dom操作时,我先做了一个getElementByTagName然后是li.appendChild等。但是现在我不知道如何在React中做到这一点,因为从我的阅读中,您不应该在react中进行dom操作。谁能帮助我通过创建适当的功能来允许按钮将内容添加到列表中吗?并删除项目吗?到目前为止,这是我的代码:
import React, { Component } from 'react';
import './App.css';
import Navigation from './components/Navigation';
import ListInput from './components/ListInput';
import ListName from './components/ListName';
class App extends Component {
constructor() {
super();
this.state = {
input: '',
items: []
};
}
addItem = () => {
this.setState(state => {
let inputValue = this.input.current.value;
if (inputValue !== '') {
this.setState({
items: [this.state.items, inputValue]
})
}
})
}
onButtonEnter = () => {
this.addItem();
}
render() {
return (
<div className="App">
<Navigation />
<ListName />
<ListInput addItem={this.addItem}
onButtonEnter={this.onButtonEnter} />
</div>
);
}
}
export default App;
这是我的ListInput组件的代码,我在其中构建输入表单和将列表信息提交到页面正文的按钮:
import React from "react";
import "./ListInput.css";
const ListInput = ({ addItem, onButtonEnter }) => {
return (
<div>
<p className="center f2">{"Enter List Item"}</p>
<div className="center">
<div className="center f3 br-6 shadow-5 pa3 ">
<input
type="text"
className="f4 pa2 w-70 center"
placeholder="Enter Here"
/>
<button
className="w-30 grow f4 link ph3 pv2 dib white bg-black"
onClick={onButtonEnter}
onSubmit={addItem}
>
{"Enter"}
</button>
</div>
</div>
</div>
);
};
export default ListInput;
我已经尝试在我的一个函数中使用.push,但是它没有用,而且我还是真的不明白。任何帮助将不胜感激,谢谢!
答案 0 :(得分:1)
您可以通过React Refs操作dom元素:
https://reactjs.org/docs/refs-and-the-dom.html
我不知道您的List组件到底在哪里,但是我想这个ListName组件会接收道具的道具,然后将它们渲染到屏幕上,所以我只是在做:
import React, { Component } from 'react';
import './App.css';
import Navigation from './components/Navigation';
import ListInput from './components/ListInput';
import ListName from './components/ListName';
class App extends Component {
constructor() {
super();
this.button = React.createRef();
this.state = {
input: '',
items: []
}
}
addToList = () => {
let inputValue = this.input.current.value;
if(inputValue !== '') {
this.setState({
items: [ ...this.state.items, inputValue ]
})
}
}
onButtonEnter = () => {
this.addToList()
}
render() {
const { items } = this.state;
return (
<div className="App">
<Navigation />
<ListName
items={ items }
/>
<ListInput addToList={this.addToList}
onButtonEnter={this.onButtonEnter} ref={ this.button } />
</div>
);
}
}
export default App;
答案 1 :(得分:1)
在react
中,我们使用声明性方式编写代码。
state
可以保存我们的数据并充当我们的真理之源state
因此,如果您有Items
的列表并想要显示它们,则将在该列表上循环并为每个项目返回Item
的可视表示形式。
要添加或删除项目时,只需在该状态下从该列表添加或删除项目。
以下是这种用法的一个运行示例:
class Item extends React.Component {
remove = () => {
const { id, onRemove } = this.props;
onRemove(id);
};
render() {
const { text } = this.props;
return (
<div style={{ display: "flex" }}>
<button onClick={this.remove}>Remove</button>
<div>{text}</div>
</div>
);
}
}
class App extends React.Component {
state = {
items: [
{ id: 1, text: "item 1" },
{ id: 2, text: "item 2" },
{ id: 3, text: "item 3" }
]
};
addItem = () => {
this.setState(state => {
const { items } = state;
const newId = uuid();
const newItem = { id: newId, text: `item ${newId}` };
return {
items: [...items, newItem]
};
});
};
onItemRemove = itemId => {
this.setState(state => {
const { items } = state;
const filteredItems = items.filter(item => item.id !== itemId);
return {
items: filteredItems
};
});
};
render() {
const { items } = this.state;
return (
<div>
<div>
<button onClick={this.addItem}>Add Item</button>
<hr />
</div>
{items.map(item => (
<Item
key={item.id}
id={item.id}
text={item.text}
onRemove={this.onItemRemove}
/>
))}
</div>
);
}
}
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.js"></script>
<div id="root"/>
修改
得知您仍然感到困惑,我们感到遗憾,但这没关系!您正在学习一项新技术,而起点总是最困难的:)
我建议您仔细阅读以"Thinking in React"开头的DOCS,并逐步进行操作。
在您最初的问题中,您问过如何“追加”或删除反应中的孩子,我认为我的回答涵盖了这一点。
至于您的新问题
我最大的问题是我不明白你和其他人如何弄清楚该怎么做...
只要您不断写作和体验,就会随着时间的流逝而得到它。
我不确定我是否完全理解在我的示例或您看到的任何其他示例中引起您困惑的原因。
据我所知,我会尝试打破您的目标:
Form
来让用户输入文字并添加新的
带有该文本的项目。现在让我们来讨论构建这个应用程序的关注点分离。
从最小的部分Item
开始。
项目
如果要显示项目列表,则可能需要一个<Item />
组件。
它的工作是什么?让我们说现在渲染一些文本。
因此该物品需要一个text
道具,看起来像这样:
const Item = ({text}) => <div>{text}</div>
好的,向上移动。
ItemList
如果要显示多个项目,则需要获取一个项目数组并在它们上循环以呈现它们。
这是react
中的常见任务,我们大多使用Array.prototype.map来完成。所以我们的组件看起来像这样:
const ItemList = ({ items }) => (
<div>
{
items.map(item => <Item key={item.id} text={item.text} />)
}
</div>
)
注意key prop。
现在可以了,但是以下哪个组件应该负责添加和删除密钥?没有!这些组件负责外观。
我们需要另一个组件来管理数据和逻辑,甚至可能根本不在乎事物的外观。
ItemListContainer
该组件需要处理添加和删除项目的逻辑,甚至可能使项目保持其状态,因此可以将其提供给ItemList
。
如果需要状态,则应为React.Component
类:
class ItemListContainer extends React.Component {
state = {
// starting with 2 items
items: [{ id: 1, text: "item 1" }, { id: 2, text: "item 2" }]
};
render() {
const { items } = this.state;
return <ItemList items={items} />;
}
}
目前,我们有一个可以显示项目的可运行应用程序:
const Item = ({ text }) => <div>{text}</div>;
const ItemList = ({ items }) => (
<div>{items.map(item => <Item key={item.id} text={item.text} />)}</div>
);
class ItemListContainer extends React.Component {
state = {
// starting with 2 items
items: [{ id: 1, text: "item 1" }, { id: 2, text: "item 2" }]
};
render() {
const { items } = this.state;
return <ItemList items={items} />;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ItemListContainer />, rootElement);
<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>
<div id="root" />
现在让我们考虑一下我们需要添加什么?
addItem
函数。input
。button
来触发addItem
功能。好,所以我们需要另一个组件来包装input
,而button
可以称之为ItemForm
。
但是让我们问问自己,ItemForm
是否需要处理添加项的逻辑? (提示!)。
保存数据并负责我们的逻辑的组件是ItemListContainer
。这是我们编写addItem
函数的地方。
但是,我们还需要从ItemForm
获取输入文本,并且仅在单击提交button
时才需要。
我认为我们也需要ItemForm
的状态。它将管理输入的更改文本,并在提交时触发传递给它的回调。
看起来像这样:
class ItemForm extends React.Component {
state = { value: "" };
onValueChange = ({ target }) => this.setState({ value: target.value });
onSubmit = () => {
const { onSubmit } = this.props;
const { value } = this.state;
// just validating empty strings
if (!value) return;
// clearing the input field
this.setState({ value: "" });
// passing the value to the callback from props
onSubmit(value);
};
render() {
const { value } = this.state;
return (
<div>
<input
placeholder="enter item text"
type="text"
value={value}
onChange={this.onValueChange}
/>
<button onClick={this.onSubmit}>Submit</button>
</div>
);
}
}
太好了!现在我们要做的就是在ItemForm
中渲染ItemListContainer
。
我们将创建一个虚拟addItem
函数,以记录该值以查看其工作原理:
class ItemListContainer extends React.Component {
state = {
// starting with 2 items
items: [{ id: 1, text: "item 1" }, { id: 2, text: "item 2" }]
};
addItem = value => {
console.log(value);
}
render() {
const { items } = this.state;
return (
<div>
<ItemForm onSubmit={this.addItem} />
<ItemList items={items} />
</div>
);
}
}
这是整个应用程序正在运行:
const Item = ({ text }) => <div>{text}</div>;
const ItemList = ({ items }) => (
<div>{items.map(item => <Item key={item.id} text={item.text} />)}</div>
);
class ItemForm extends React.Component {
state = { value: "" };
onValueChange = ({ target }) => this.setState({ value: target.value });
onSubmit = () => {
const { onSubmit } = this.props;
const { value } = this.state;
// just validating empty strings
if (!value) return;
// clearing the input field
this.setState({ value: "" });
// passing the value to the callback from props
onSubmit(value);
};
render() {
const { value } = this.state;
return (
<div>
<input
placeholder="enter item text"
type="text"
value={value}
onChange={this.onValueChange}
/>
<button onClick={this.onSubmit}>Submit</button>
</div>
);
}
}
class ItemListContainer extends React.Component {
state = {
// starting with 2 items
items: [{ id: 1, text: "item 1" }, { id: 2, text: "item 2" }]
};
addItem = value => {
console.log(value);
}
render() {
const { items } = this.state;
return (
<div>
<ItemForm onSubmit={this.addItem} />
<ItemList items={items} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ItemListContainer />, rootElement);
<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>
<div id="root" />
好,现在使用addItem
逻辑。众所周知,项目是处于状态的数组,如果要添加新项目,我们要做的就是将其添加到数组中。
我们不会使用this.state.push(newItem)
,因为这会使数组发生变异,这在我们的反应世界中是一个很大的禁忌。
因此我们的addItem
函数可能类似于:
addItem = value => {
this.setState(state => {
const { items } = state;
// watch it we are missing an id property here!!!
const newItem = { text: value };
return {
items: [...items, newItem]
};
});
};
如您所见,这很简单,只需使用item
作为value
属性创建一个新的text
。但是请注意,我们没有提供id
属性,我们将在key
的{{1}}道具中使用该属性,以后在删除{{1 }}(我们将根据<Item />
将其删除)。
在这里,您有两种选择,我将选择uuid
查看带有新逻辑(还添加了item
)的运行示例:
id
uuid
删除项目
这并不比添加项目难,我们只需要两件事:
const Item = ({ text }) => <div>{text}</div>;
const ItemList = ({ items }) => (
<div>{items.map(item => <Item key={item.id} text={item.text} />)}</div>
);
class ItemForm extends React.Component {
state = { value: "" };
onValueChange = ({ target }) => this.setState({ value: target.value });
onSubmit = () => {
const { onSubmit } = this.props;
const { value } = this.state;
// just validating empty strings
if (!value) return;
// clearing the input field
this.setState({ value: "" });
// passing the value to the callback from props
onSubmit(value);
};
render() {
const { value } = this.state;
return (
<div>
<input
placeholder="enter item text"
type="text"
value={value}
onChange={this.onValueChange}
/>
<button onClick={this.onSubmit}>Submit</button>
</div>
);
}
}
class ItemListContainer extends React.Component {
state = {
// starting with 2 items
items: [{ id: 1, text: "item 1" }, { id: 2, text: "item 2" }]
};
addItem = value => {
this.setState(state => {
const { items } = state;
const newId = uuid();
const newItem = { text: value, id: newId };
return {
items: [...items, newItem]
};
});
};
render() {
const { items } = this.state;
return (
<div>
<ItemForm onSubmit={this.addItem} />
<ItemList items={items} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ItemListContainer />, rootElement);
旁边的<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.js"></script>
<div id="root" />
。button
函数,该函数将接受
特定的Item
。为此,我们将需要稍微更改removeItem
组件。
item
如您所见,每个Item
现在都会收到另一个道具const Item = ({ text, onRemove }) => (
<div>
<button onClick={onRemove}>X</button>
{text}
</div>
);
。
我们将需要从Item
传递它,它是呈现每个onRemove
的组件:
ItemList
请注意,Item
也如何获得const ItemList = ({ items, onRemove }) => (
<div>
{items.map(item => (
<Item key={item.id} text={item.text} onRemove={() => onRemove(item.id)} />
))}
</div>
);
道具,而不仅仅是传递它。它使用匿名函数向下传递,并传递ItemList
作为参数。记得?我们说过,我们将需要此onRemove
才能知道要删除哪个id
。
因此,使用新的id
函数,看看我们的item
现在是什么样子:
ItemListContainer
我们仅记录onRemove
只是为了查看其工作情况。
这是运行代码:
class ItemListContainer extends React.Component {
state = {
// starting with 2 items
items: [{ id: 1, text: "item 1" }, { id: 2, text: "item 2" }]
};
addItem = value => {
this.setState(state => {
const { items } = state;
const newId = uuid();
const newItem = { text: value, id: newId };
return {
items: [...items, newItem]
};
});
};
onRemove = id => console.log(id);
render() {
const { items } = this.state;
return (
<div>
<ItemForm onSubmit={this.addItem} />
<ItemList items={items} onRemove={this.onRemove} />
</div>
);
}
}
id
现在我们已经拥有了所有的东西,我们可以编写我们的逻辑了。这很简单,就像使用Array.prototype.filter()一样:
const Item = ({ text, onRemove }) => (
<div>
<button onClick={onRemove}>X</button>
{text}
</div>
);
const ItemList = ({ items, onRemove }) => (
<div>
{items.map(item => (
<Item key={item.id} text={item.text} onRemove={() => onRemove(item.id)} />
))}
</div>
);
class ItemForm extends React.Component {
state = { value: "" };
onValueChange = ({ target }) => this.setState({ value: target.value });
onSubmit = () => {
const { onSubmit } = this.props;
const { value } = this.state;
// just validating empty strings
if (!value) return;
// clearing the input field
this.setState({ value: "" });
// passing the value to the callback from props
onSubmit(value);
};
render() {
const { value } = this.state;
return (
<div>
<input
placeholder="enter item text"
type="text"
value={value}
onChange={this.onValueChange}
/>
<button onClick={this.onSubmit}>Submit</button>
</div>
);
}
}
class ItemListContainer extends React.Component {
state = {
// starting with 2 items
items: [{ id: 1, text: "item 1" }, { id: 2, text: "item 2" }]
};
addItem = value => {
this.setState(state => {
const { items } = state;
const newId = uuid();
const newItem = { text: value, id: newId };
return {
items: [...items, newItem]
};
});
};
onRemove = id => console.log(id);
render() {
const { items } = this.state;
return (
<div>
<ItemForm onSubmit={this.addItem} />
<ItemList items={items} onRemove={this.onRemove} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ItemListContainer />, rootElement);
现在我们可以正常运行了,您可以在这里看到它(是!):
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.js"></script>
<div id="root" />
onRemove = id => {
this.setState(state => {
const { items } = state;
const filteredItems = items.filter(item => item.id !== id);
return { items: filteredItems };
});
};
请注意,示例中没有内联匿名函数之类的最佳实践。但现在不用担心,专注于学习基础知识
我希望这可以消除您的困惑,并为您学习这项出色的技术提供一个良好的起点。 :)