8

I'm new to React and made an app that allows searches to be saved. This will pull JSON but is currently pulling from a static array data. I'm having trouble being able to delete searches from the search list.

Here's the jsbin: http://jsbin.com/nobiqi/edit?js,output

Here's my delete button element:

var DeleteSearch = React.createClass({
  render: function() {
    return (
      <button onClick="this.props.deleteSearchItem" value={index}><i className="fa fa-times"></i>
        </button>
    );
  }
});

and my function

  deleteSearchItem: function(e) {
    var searchItemIndex = parseInt(e.target.value, 10);
    console.log('remove task: %d', searchItemIndex);
    this.setState(state => {
        state.data.splice(searchItemIndex, 1);
        return { data: state.data };
    });
  }

I've tried following tutorials and I'm not sure where to go from here. How can I delete the search items?

Virge Assault
  • 1,356
  • 6
  • 18
  • 40
  • 3
    `onClick="this.props.deleteSearchItem"` that doesn't look right. Expressions go in between braces, just like you did in `value={index}` – elclanrs Mar 09 '16 at 20:31
  • like `onClick={this.props.deleteSearchItem}`? I'm new to the syntax so that's very helpful. – Virge Assault Mar 09 '16 at 20:34
  • 4
    Take half an hour to go to http://facebook.github.io/react/docs/tutorial.html and just run through the whole thing. No skipping sections, just do what it says start to finish. Whether you're new to web dev or a 10+ years seasoned professional, that tutorial is *fantastic* at teaching you the basics so you never have to ask these kind of questions again. – Mike 'Pomax' Kamermans Mar 09 '16 at 21:00
  • @Mike'Pomax'Kamermans I did go through it extensively, a lot of the code above was written from the docs. But I came here bc I was still having trouble. Have to get through a piece of functionality off the docs to really understand. – Virge Assault Mar 09 '16 at 21:09
  • 1
    Did you read it, or did you *do* it? Because just reading it is not following the tutorial. If you followed the tutorial, you would have gone over the http://facebook.github.io/react/docs/tutorial.html#controlled-components section, too, which teaches you how to reference component functions for event handling. – Mike 'Pomax' Kamermans Mar 09 '16 at 21:11
  • I did it, and I'm having a hard time understanding so I asked for help. Thank you for pointing out a specific section. – Virge Assault Mar 09 '16 at 21:23
  • Possible duplicate of [How can I remove an attribute from a Reactjs component's state object](http://stackoverflow.com/questions/32884780/how-can-i-remove-an-attribute-from-a-reactjs-components-state-object) – Vince Mar 10 '17 at 06:25
  • The answer by @the-reason below is wrong as it results in modifying state directly. This question has been asked and answered before, though. Read carefully... the accepted answers aren't correct either: http://stackoverflow.com/a/42711673/2948042 – Vince Mar 10 '17 at 06:28

3 Answers3

26

Let me guess, Are you looking for something like this?

class Example extends React.Component {
    constructor(){
    this.state = {
      data: [
        {id:1, name: 'Hello'},
        {id:2, name: 'World'},
        {id:3, name: 'How'},
        {id:4, name: 'Are'},
        {id:5, name: 'You'},
        {id:6, name: '?'}
      ]
    }
  }

  // shorter & readable 
  delete(item){
    const data = this.state.data.filter(i => i.id !== item.id)
    this.setState({data})
  }

  // or this way, it works as well
  //delete(item){
  //  const newState = this.state.data.slice();
  //    if (newState.indexOf(item) > -1) {
  //    newState.splice(newState.indexOf(item), 1);
  //    this.setState({data: newState})
  //  }
  //}

  render(){
    const listItem = this.state.data.map((item)=>{
        return <div key={item.id}>
        <span>{item.name}</span> <button onClick={this.delete.bind(this, item)}>Delete</button>
      </div>
    })
    return <div>
        {listItem}
    </div>
  }
}

React.render(<Example />, document.getElementById('container'));

In this example pay attention how i'm binding delete method and pass there new parameter. fiddle

I hope it will help you.

Thanks

The Reason
  • 7,705
  • 4
  • 24
  • 42
  • I doubt this is very performant for long lists..my guess is storing an id as a string in the list item and then retrieving that is more performant, and perhaps there is a more "native" way to do this with React? – Alexander Mills Mar 10 '16 at 00:08
  • @AlexMills in this case you should look at Flux or Redux architecture – The Reason Mar 10 '16 at 16:07
  • 2
    `const newState = this.state.data;` doesn't copy of the data object in your state. It just stores a reference to `this.state.data` in `newState`. So, when you `splice()` an item out, you're directly modifying `this.state.data`. That's [the wrong way](https://facebook.github.io/react/docs/state-and-lifecycle.html#do-not-modify-state-directly). I prove it simply by commenting the `setState` line in my fork of your [fiddle](http://jsfiddle.net/VAggrippino/v0wh6h5r/) As for the right way, I don't know... That's what I came here to find. – Vince Mar 10 '17 at 05:14
  • 1
    @Vince, yeap you are right. I should have used one of these options `Array.prototype.slice` or `spread` operator for copying new `state` – The Reason Dec 18 '17 at 16:41
4

OP here. Since I know more about React four years later and this still gets views I figured I'd update this with how I'd go about it now.

SavedSearches.js

import React from 'react'
import { SearchList } from './SearchList'

let data = [
    {index: 0, name: "a string", url: 'test.com/?search=string'},
    {index: 1, name: "a name", url: 'test.com/?search=name'},
    {index: 2, name: "return all", url: 'test.com/?search=all'}
  ];

let startingIndex = data.length;

export class SavedSearches extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '',
            url: '',
            index: startingIndex,
            data: data
        }
        this.deleteSearch=this.deleteSearch.bind(this)
    }
    deleteSearch(deleteThis) {
        console.log(deleteThis);
        let newData = this.state.data.filter( searchItem => searchItem.index !== deleteThis.index )
        this.setState({
            data: newData
        })
    }

    render() {
        return (
            <div className="search-container">
                <SearchList data={this.state.data} onDelete={this.deleteSearch}/>
            </div>
        )
    }
}

Here I created a method called deleteSearch that takes an object as a parameter. It then runs .filter on the this.state.data array to create a new array that includes all items that don't meet the condition. The condition checks if the id of each object in the data array matches the id of the parameter. If so, then it is the one that is being deleted. The new array that is created by .filter is set to a variable called newData, and then I update the state with the newData array.

I then pass this method to the SearchList component in a prop called onDelete.

This method is also bound in the constructor using .bind() so that this will refer to the correct this when the method is passed down the component tree.

SearchList.js

import React from 'react'
import { SearchItem } from './SearchItem'
export class SearchList extends React.Component {
    render() {
      let searchItems = this.props.data.map((item, i) => {
        return (
          <SearchItem index={i} searchItem={item} url={item.url} onDelete={this.props.onDelete}>
            {item.name}
          </SearchItem>
        );
      });
      return (
        <ul>
          {searchItems}
        </ul>
      );
    }
}

My deleteSearch method is just passing through the component tree here. SearchList receives the method as a props this.props.onDelete and passes it to SearchItem.

The other major key here is that the parameter in the map function is being passed as props: searchItem={item}. This will allow the entire current object to be accessed via props; and if you remember, my deleteSearch function takes an object as a parameter.

SearchItem.js

import React from 'react'

export class SearchItem extends React.Component {
    constructor(props) {
        super(props);
        this.handleDelete=this.handleDelete.bind(this)
    }
    handleDelete() {
        this.props.onDelete(this.props.searchItem)
    }
    render() {
      return (
        <li key={this.props.index}> {/* Still getting a console error over this key */}
          <a href={this.props.url} title={this.props.name}>
            {this.props.children}
          </a>
          &nbsp;({this.props.url})
          <button onClick={this.handleDelete} value={this.props.index}><i className="fa fa-times"></i>
          </button>
        </li>
      );
    }
  };

Now my method arrives where it will be used. I create a handler method handleDelete and inside I access the deleteSearch method with this.props.onDelete. I then pass it the object of the list item that is being clicked on with this.props.searchItem.

In order for this to work when a user clicks, I had to add an onClick event listener that calls my handler method, like this: onClick={this.handleDelete}. The final step is to bind this.handleDelete in the SearchItem constructor method.

Now, clicking on the button will remove the item from the this.state.data array. For an example of how to add an item to the array, see my repository

Virge Assault
  • 1,356
  • 6
  • 18
  • 40
0

Are you looking for somthing like this?

Todos.js

import React from 'react'
import {TodoItem} from "./TodoItem";

export const Todos = (props) => {

    let myStyle = {
        minHeight: "70vh",
        margin: "40px auto"
    }
    return (
        <div className="container" style={myStyle}>
            <h3 className="my-3">List</h3>
            {props.todos.length===0? "No records to display":  
            props.todos.map((todo)=>{
                console.log(todo.sno);
                return (<TodoItem todo={todo} key={todo.sno} onDelete={props.onDelete}/>   
                )
            })
              } 
        </div>
    )
}

TodoItem.js

import React from 'react'

export const TodoItem = ({todo, onDelete}) => {

    return (
        <>
        <div>
           <h4>{todo.title}</h4>
           <p>{todo.desc}</p>
           <button className="btn btn-sm btn-danger" onClick={()=>{onDelete(todo)}}>Delete</button> 
        </div>
        <hr/> 
        </>
    )
}

Please see the repository, here you can find add ,delete and list items

Narendra
  • 21
  • 3