如何在React中创建appenChild或删除按钮功能

时间:2018-09-26 17:32:09

标签: javascript reactjs

好!我在这里列出了这个问题的更简洁示例: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,但是它没有用,而且我还是真的不明白。任何帮助将不胜感激,谢谢!

2 个答案:

答案 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 };
  });
};


请注意,示例中没有内联匿名函数之类的最佳实践。但现在不用担心,专注于学习基础知识

我希望这可以消除您的困惑,并为您学习这项出色的技术提供一个良好的起点。 :)