0

I have a component to display list of tags, and the user can select tags to follow them. The tags are displaying fine. I would like to get the selected tag and store it inside a new array tagsSelectedList. So, when the user clicks a Tag, I would like to get that tag and push it to tagsSelectedList. However I am getting an error after I placed an onClick inside the li of the map function.

return (
    <li id={Tag.tagName} class="tag" key={Tag.id} onClick={this.selectTag}>{Tag.tagName}</li>
);

This is the error:

Uncaught TypeError: Cannot read property 'selectTag' of undefined

Component.js:

let tags = [
    {id: "1", tagName: "Arts"},
    ...
    ...
    {id: "59", tagName: "Writing"}
}];

var tagsSelectedList = [];

export default class SignUpSubscribeTags extends React.Component {
    constructor(props){
        super(props);
    }

    selectTag = (e) => {
        console.log(e.target.id);
    }

    render() {
        let tagList = tags.map(function(Tag){
            var i = 0;
            return (
                <li id={Tag.tagName} class="tag" key={Tag.id} onClick={this.selectTag}>{Tag.tagName}</li>
            );
        });

        return(
            <div id="signup-process-wrapper-addTags">

                <div id="add_tags">
                    <ul id="tag_list">
                        {tagList}
                    </ul>
                </div>
            </div>
        );
    }
}

However if I remove onClick={this.selectTag} from the return statement of tagList,

<li id={Tag.tagName} class="tag" key={Tag.id}>{Tag.tagName}</li>

and place a li with an onClick={this.selectTag} inside the ul,

                <ul id="tag_list">
                    <li id="tagName" class="tag" onClick={this.selectTag}>tagName</li>
                    {tagList}
                </ul>

it works fine! I get no error.

What am I doing wrong? Please help me. Thank you.

1 Answer 1

4

You need to scope this so it references the React component context

There are a couple of ways to do this:

Option 1:

Using bind()

render() {
    let tagList = tags.map(function(Tag){
        var i = 0;
        return (
            <li id={Tag.tagName} class="tag" key={Tag.id} onClick={this.selectTag}>{Tag.tagName}</li>
        );
    }.bind(this));

    return(
        <div id="signup-process-wrapper-addTags">

            <div id="add_tags">
                <ul id="tag_list">
                    {tagList}
                </ul>
            </div>
        </div>
    );
}

Option 2:

You can use the ES6 arrow function to scope it

    let tagList = tags.map((Tag) => {
        var i = 0;
        return (
            <li id={Tag.tagName} class="tag" key={Tag.id} onClick={this.selectTag}>{Tag.tagName}</li>
        );
    });

Option 3:

The map function also takes a second argument that specifies what this refers to

    let tagList = tags.map(function(Tag) {
        var i = 0;
        return (
            <li id={Tag.tagName} class="tag" key={Tag.id} onClick={this.selectTag}>{Tag.tagName}</li>
        );
    }, this);
Sign up to request clarification or add additional context in comments.

2 Comments

Yes. Working. Thank you.
My pleasure, this can be a confusing concept in javascript, I recommend you put aside the time to understand why the above is necessary and how lexical scoping works, it will save you from a lot of frustration in the future.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.