ReactJS,Todo列表,如何从列表

时间:2017-10-22 16:17:27

标签: reactjs

我是ReactJS新手(嗯,具体我是编程新手)寻找一些答案,请不要因为明显的愚蠢而排除我:)我在传递道具和理解'这个'时遇到了很大麻烦上下文。

这是我的混乱,两个简单的Todo应用程序组件。 TodoApp:

import React from 'react';
import uuid from 'uuid';
import style from './App.css';
import Title from '../components/Title.js';
import TodoList from '../components/TodoList.js';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.title = "Todo Application"
        this.state = {
            data: [{
                id: 1,
                text: 'clean room'
                }, {
                id: 2,
                text: 'wash the dishes'
                }, {
                id: 3,
                text: 'feed my cat'
            }]
        };
    }

    removeTodo(id) {
        const remainder = this.state.data.filter(item => item.id !== id);
        this.setState({data: remainder});
    }

    render() {
        return (
            <div className={style.TodoApp}>
                <Title title="Todo Application" added={this.state.data.length} />
                <TodoList data={this.state.data} remove={this.removeTodo}></TodoList>
            </div>
        );
    }
}

export default App;

和TodoList:

import React from 'react';

const TodoList = props => (
    <ul>
        {props.data.map((item, index) =>
            <li key={index}>
                {item.text}
                <button value={index} onClick={() => props.remove(index)}>x</button>
            </li>
        )}
    </ul>
);

export default TodoList;

我的问题是如何正确地将道具传递给子组件(TodoList),以便删除按钮可以工作?

2 个答案:

答案 0 :(得分:1)

要确保this始终引用定义了removeTodo方法的App上下文,您可以在构造函数中添加以下内容(在设置初始状态之后):

this.removeTodo = this.removeTodo.bind(this);

否则,你的代码根本就不凌乱。它甚至令人惊讶地简洁和深思熟虑,对于一个自称为初学者来说更是如此。恭喜!

正如Sag1v在下面的评论中指出的那样,您还没有将正确的值传递给removeTodo方法。您正在传递正在迭代的项目的索引,而不是其ID。

将您的<button>调用更改为以下内容:

<button value={index} onClick={() => props.remove(item.id)}>x</button>

请注意,您也可以通过以下方式实现相同目标:

<button value={index} onClick={props.remove.bind(null, item.id)}>x</button>

答案 1 :(得分:1)

这里有两个主要问题:

  1. 正如Jaxx所说,你没有将处理程序removeTodo绑定到 class 有几种方法可以做到这一点。

    • 将其绑定在constructor

      this.removeTodo = this.removeTodo.bind(this);

    • 或者使用将this使用词汇上下文的ES2015(ES6) Arrow functions

      removeTodo = (id) => {
         const remainder = this.state.data.filter(item => item.id !== id);
         this.setState({data: remainder});
      }
      
  2. 第二个问题是你在TodoList onClick内部处理 没有将正确的id传递给处理程序,你正在传递 指数位置。

    onClick={() => props.remove(index)}
    

    您应该将其更改为:

    onClick={() => props.remove(item.id)} 
    

    这种方法还有另一个问题,我将在下面解释。

  3. 这是一个有效的例子:

    const Title = ({title}) => <h1>{title}</h1>
    
    const TodoList = props => (
        <ul>
            {props.data.map((item, index) =>
                <li key={index}>
                    {item.text}
                    <button value={index} onClick={() => props.remove(item.id)}>x</button>
                </li>
            )}
        </ul>
    );
    
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.title = "Todo Application"
            this.state = {
                data: [{
                    id: 1,
                    text: 'clean room'
                    }, {
                    id: 2,
                    text: 'wash the dishes'
                    }, {
                    id: 3,
                    text: 'feed my cat'
                }]
            };
        }
    
        removeTodo = (id) => {
            const remainder = this.state.data.filter(item => item.id !== id);
            this.setState({data: remainder});
        }
    
        render() {
            return (
                <div>
                    <Title title="Todo Application" added={this.state.data.length} />
                    <TodoList data={this.state.data} remove={this.removeTodo}></TodoList>
                </div>
            );
        }
    }
    
    ReactDOM.render(<App/>,document.getElementById('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>
    <div id="root"></div>

    正如我所说的,这种方法存在问题,您使用以下代码行在每个渲染上传递一个函数的新实例:

    onClick={() => props.remove(item.id)
    

    这被认为是不好的做法,因为这会中断Reconciliation and diffing algorithm

    但是正如我们所知,event处理程序应该获得一个函数引用,因此你不能只传递一个像这样的函数调用

    onClick={props.remove(item.id)}
    

    这将传递函数的返回类型(如果有),而不是函数的引用 所以传递函数引用的正确方法是这样的:

    onClick={props.remove}
    

    但这对你的情况不利,因为你需要将当前项id传回给父,但我担心浏览器只会传回event参数。< / p>

    那么你问的替代方案是什么?

    创建另一个组件并控制您从组件传入和传出的数据,而不是依赖于浏览器的商誉。

    这是另一个工作示例,但这次没有在每个渲染上创建一个新的函数实例

    const Title = ({title}) => <h1>{title}</h1>
    
    class TodoItem extends React.Component {
      handleClick = () => {
        const{item, onClick} = this.props;
        onClick(item.id);
      }
    
      render(){
        const {item} = this.props;
        return(
          <li>
            {item.text}
            <button value={item.id} onClick={this.handleClick}>x</button>
          </li>
        );
      }
    }
    
    const TodoList = props => (
        <ul>
            {props.data.map((item, index) =>
                <TodoItem key={index} item={item} onClick={props.remove} />
            )}
        </ul>
    );
    
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.title = "Todo Application"
            this.state = {
                data: [{
                    id: 1,
                    text: 'clean room'
                    }, {
                    id: 2,
                    text: 'wash the dishes'
                    }, {
                    id: 3,
                    text: 'feed my cat'
                }]
            };
        }
    
        removeTodo = (id) => {
            const remainder = this.state.data.filter(item => item.id !== id);
            this.setState({data: remainder});
        }
    
        render() {
            return (
                <div>
                    <Title title="Todo Application" added={this.state.data.length} />
                    <TodoList data={this.state.data} remove={this.removeTodo}></TodoList>
                </div>
            );
        }
    }
    
    ReactDOM.render(<App/>,document.getElementById('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>
    <div id="root"></div>