kayak
kayak

Reputation: 438

React: Empty element is added (perhaps because of SVG)

I'm making Add to favorite button in React. When I click on an icon, the element beside the icon will be added to likes array. So far it's working, but sometimes empty element is added to likes array, perhaps because of SVG icon. 90% of the time, event target is 'path' so 'e.target.parentNode.parentNode.id;' works just fine. However when the event is detected on either or , that parentNode thing won't work, and it adds an empty element to the array. How do I fix this problem?

class App extends React.Component {

    state = {
        data: [
            { 
                "breed": "Beagle",
                "characteristic": "playful" 
            },
            {
                "breed": "Golden Retriever", 
                "characteristic": "calm" 
            },
            {
                "breed": "Corgi", 
                "characteristic": "bright" 
            },
            {
                "breed": "Goldendoodle", 
                "characteristic": "gentle" 
            },
            {
                "breed": "Labrador Retriever", 
                "characteristic": "loyal" 
            },
        ],
        likes: []      
    }

    handleClick = e => {
        e.preventDefault();

        const breed = e.target.parentNode.parentNode.id;
        const index = this.state.likes.indexOf(breed);    

        if (index === -1) {
            this.state.likes.push(breed);
            this.renderLikes(this.state.likes);           
        } else {
            this.state.likes.splice(index, 1); 
            this.renderLikes(this.state.likes);                     
        }
    }

    likesTemplate = item => `<li>${item}</li>`;

    renderLikes = () => {
        this.refs.fav.innerHTML = this.state.likes.map(i => this.likesTemplate(i)).join('');
    }

    render() {


        return (
            <div>                
                <ul>

       {this.state.data.map(index => {
         return <li key={index.breed} id={index.breed}>
             <IcoMoon icon="heart" onClick={this.handleClick} />{index.breed}</li>;

       })}
       </ul>

       <h2>Favorite Dogs</h2>
       <ul ref="fav">

        </ul>
            </div>
        );
    }
}

export default App;

Upvotes: 0

Views: 294

Answers (2)

Junius L
Junius L

Reputation: 16132

Pass a breed to your handleClick function.

handleClick = (breed, index) => {

  if (this.state.likes.filter(e => e.breed === breed.breed).length > 0) {

    this.setState({
      likes: this.state.likes.filter(e => e.breed !== breed.breed)
    });

    return;
  }

  this.setState({
    likes: [...this.state.likes, breed]
  });
};

likesTemplate = item => <li>{item.breed}</li>;

renderLikes = () => {
  return this.state.likes.map(i => this.likesTemplate(i));
};

Your render method

render() {
  return (
    <div>
      <ul>
        {this.state.data.map(item => {
          return (
            <li
              key={item.breed}
              id={item.breed}
            >
              <IcoMoon icon="heart" onClick={() => this.handleClick(item)}/>
              {item.breed}
            </li>
          );
        })}
      </ul>

      <h2>Favorite Dogs</h2>
      <ul ref="fav">{this.renderLikes()}</ul>
    </div>
  );
}

<script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>

<script type="text/babel">

class App extends React.Component {
  state = {
    data: [
      {
        breed: "Beagle",
        characteristic: "playful"
      },
      {
        breed: "Golden Retriever",
        characteristic: "calm"
      },
      {
        breed: "Corgi",
        characteristic: "bright"
      },
      {
        breed: "Goldendoodle",
        characteristic: "gentle"
      },
      {
        breed: "Labrador Retriever",
        characteristic: "loyal"
      }
    ],
    likes: []
  };

    handleClick = (breed, index) => {

      if (this.state.likes.filter(e => e.breed === breed.breed).length > 0) {
        this.setState({
          likes: this.state.likes.filter(e => e.breed !== breed.breed)
        });
        
        return;
      }

      this.setState({
        likes: [...this.state.likes, breed]
      });
    };

    likesTemplate = item => <li>{item.breed}</li>;

    renderLikes = () => {
      return this.state.likes.map(i => this.likesTemplate(i));
    };

    render() {
      return (
        <div>
          <ul>
            {this.state.data.map((item, index) => {
              return (
                <li
                  key={item.breed}
                  onClick={() => this.handleClick(item, index)}
                  id={item.breed}
                >
                  <div icon="heart" />
                  {item.breed}
                </li>
              );
            })}
          </ul>

          <h2>Favorite Dogs</h2>
          <ul ref="fav">{this.renderLikes()}</ul>
        </div>
      );
    }
}

ReactDOM.render(<App />, document.getElementById('root'));
</script>

Upvotes: 1

marsheth
marsheth

Reputation: 960

Since you're already passing the breed to the list item, it'd probably be easiest for you to just pass that breed using react instead of reaching into the DOM:

...

    handleClick = (e, breed) => {
        e.preventDefault();

        const index = this.state.likes.indexOf(breed);    

        if (index === -1) {
            this.state.likes.push(breed);
            this.renderLikes(this.state.likes);           
        } else {
            this.state.likes.splice(index, 1); 
            this.renderLikes(this.state.likes);                     
        }
    }

    likesTemplate = item => `<li>${item}</li>`;

    renderLikes = () => {
        this.refs.fav.innerHTML = this.state.likes.map(i => this.likesTemplate(i)).join('');
    }

    render() {


        return (
            <div>                
                <ul>

       {this.state.data.map(index => {
         return <li key={index.breed} id={index.breed}>
             <IcoMoon icon="heart" onClick={(e) => this.handleClick(e, index.breed} />{index.breed}</li>;

       })}

....

Upvotes: 2

Related Questions