1

how to get the 'e.key' of li element in react.js?

I just can't get the event target e.key, what's wrong with this?

Anybody can help me with this problem?

online playaround link!

https://jscomplete.com/repl

// import React, {Component} from 'react';
// import PropTypes from 'prop-types';

class RMT extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: this.props.data || []
        };
    }
    // 递归调用
    markNode = (data) => {
        let nodes;
        if (Object.prototype.toString.call(data) == "[object Array]") {
            nodes = data.map(
                (item) => {
                    let node = (
                        <li
                            key={this.count++}
                            style={{color: 'red'}}
                            onClick={(e) => this.props.onClick(e)}
                            >
                            <span>{item.text}</span>
                        </li>
                    );
                    if (item.children && item.children.length) {
                        node = (
                            <li
                                key={this.count++}
                                style={{color: 'green', marginLeft: '10px'}}
                                onClick={(e) => this.props.onClick(e)}
                                >
                                <span>{item.text}</span>
                                {this.markNode(item.children)}
                            </li>
                        );
                    }
                    return node;
                }
            );
        }
        return (
            <ul style={{border: '1px solid red'}} onClick={this.props.onClick}>
                {nodes}
            </ul>
        );
    };
    render() {
        this.count = 0;
        return(
            this.markNode(this.state.data)
        );
    }
}

export {RMT};
export default RMT;

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            // 
        };
    }
    onClick = (e) => {
        // 阻止事件的默认行为,防止事件冒泡!
        e.preventDefault();
        e.stopPropagation();
        // alert(`e`, e.key);
        console.log('e.key', e.key);
        console.log('e.text', e.text);
        console.log('e.innerHTML', e.innerHTML);
        console.log('e.innerText', e.innerText);
    };
    render () {
        const datas = [
            {
                text: 'root',
                children: [
                    {
                        text: 'chlid1',
                        children: [
                            {
                                text: 'chlid3'
                            }
                        ]
                    }
                ]
            }
        ];
        return (
            <div>
                <RMT data={datas} onClick={this.onClick}/>
            </div>
        );
    }
}

export default App;

ReactDOM.render(
    <div>
        <App />
    </div>
    , mountNode
);
<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>

4 Answers4

3

A few things to note here that will hopefully point you in the right direction:

1) It is ambiguous to what key you refer. In React a component's key is not publicly accessible, this is by design. However as you have logged e.key you may be trying to access the keyboard key of the event itself - however this is not present on a mouse event, only a keyboard event.

2) Your onClick handler that logs out values does not act in exactly the same manner as a standard DOM event, the event actually comes from React's own internal synthetic event system, and the scope of your handler (ie this) is the class to which it is lexically bound, not as in a standard DOM event where the scope is the element that triggered it (your App.onClick is lexically bound by the arrow function to the instance of App)

You can still access the wrapped event, and the DOM node that caused it by properties of Reacts own synthetic event...

onClick = (e) => { // types are... e: SyntheticEvent<MouseEvent> , this: App
    e.preventDefault()
    e.stopPropagation()
    // you can access the native DOM event this wraps via the `nativeEvent` to call methods or access event properties that React does not proxy. This is not recommended though as React has done a lot of work to consolidate any browser quirks, which accessing native event like this would negate, so you'ld have to handle that yourself
    e.nativeEvent.stopImmediatePropagation()

    // access the actual DOM node that triggered the event via `target`
    console.log(e.target.innerHTML);
    // access the DOM node that the event is currently the current event phase is acting upon via `currentTarget`
    console.log(e.target.innerHTML);

    // if you REALLY need some data to be available via the DOM itself, then you can it via the DOM node's `data` attributes (ie data-nic-cage={someString}, and access it like so...
    console.log(e.target.dataset.nicCage)
}

3) When assigning your key you are incrementing this.count which means that no 2 keys will be the same on subsequent renders. Your render function is impure, and will cause React to be unable to reconcile subsequent rendered child components to current child components, and instead always unmount then remount new ones causing unnecessary re-renders and losing child state - hurting performance, and potentially introducing bugs in the child components. Unless it has a greater meaning to you (ie forcing reanimation to occur) then you can just pass the map index (although a stable id would be preferable but I can't see anything suitable there on item).

data.map((item, i) => <li key={i} /*other props*/ />)
alechill
  • 4,274
  • 18
  • 23
  • 1
    In React a component's key is not publicly accessible, this is by design. –  Jul 19 '17 at 02:30
2

i don't think you can access key attribute in react. If you need to pass the value, you can use another prop name such as indexKey or anything else.

  • Yeah, customized prop key `indexkey`! –  Jul 19 '17 at 02:46
  • I use typeScript so I couldn't use `indexKey` or some customized props like that, so I use `data-key` and it works fine to me, I got the value by `e.target.dataset.key` in onclick function – marzzy Jun 21 '21 at 07:57
0

You need bind your function onClick in App component

    class App extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                // 
            };
            this.onClick = this.onClick.bind(this);
        }
Simonov Dmitry
  • 457
  • 2
  • 6
0

this is a demo to show that, in react.js you can't access e.key directly!

                <Message
                    key={index}
                    indexkey={index}
                    text={message.text}
                    color={message.color}
                    xyzClick={this.props.xyzClick}
                />
                this.props.indexkey = {this.props.indexkey}

online playground!

https://jscomplete.com/repl

enter image description here

// props is an object!


// message.text => array of objects
class MessageList extends React.Component {
    getChildren = () => {
        //
    }
    render() {
        const children = this.props.messages.map(
            (message, index) => {
                //console.log(`message = `, message);
              console.log(`index = `, index);
                //console.log(`typeof index = `, typeof index);
                let xindex = 'id_' + index; 
                console.log(`xindex = `, xindex);
                //console.log(`typeof xindex = `, typeof xindex);
                return(
                    <Message
                        key={index}
                        text={message.text}
                        color={message.color}
                        xyzClick={this.props.xyzClick}
                    />
                );
            }
        );
        const styles = {
          css1: {
              color: 'red'
            },
            css2: {
              color: '#0f0'
            }
        }
       return(
            <div>
                children = {children}
                <hr />
                <div>
                    BAD: <br />
                    {/* this.props = {this.props} */}
                    <div style={styles.css1}>
                        this.props.children = {this.props.color}
                    </div>
                    {/* this.props.arr = {this.props.arr} */}
                    {/* this.props.obj = {this.props.obj} */}
                    <br />
                    <p style={styles.css2}>
                        Object Error, need using map items to item
                    </p>
                </div>
            </div>
        );
    }
}

// text={message.text} => message object
class Message extends React.Component {
    render() {
      //console.log(`this.props.text = `, this.props.text);
        //console.log(`this.props.key= `, this.props.key);
        let style = `
          color: red;
            font-size: 23px;
        `;
        if(this.props.key === undefined){
          //alert(`smg!`);
            console.log(`%c this.props.key= \n`, style, this.props.key);
        }
        return (
            <div>
                <hr />
                this.props.key = {this.props.key}
                <br />
                this.props.text = {this.props.text}
                <br />
                this.props.color = <span style={{backgroundColor: this.props.color}}>{this.props.color}</span>
                <br />
                <Button color={this.props.color} xyzClick={this.props.xyzClick}>
                    <span style={{color: '#fff'}}>Delete</span>
                </Button>
            </div>
        );
    }
}

// props.children === <span style={{color: '#fff'}}>Delete</span> ??? 
class Button extends React.Component {
    render() {
        return (
            <button
                style={{background: this.props.color}} 
                onClick={(e) => this.props.xyzClick(e)} 
                >
                {this.props.children}
            </button>
        );
    }
}




const text = [
    {
    text: "text 1",
    color: "red"
    },
    {
    text: "text 2",
    color: "blue"
    },
    {
    text: "text 3",
    color: "grey"
    },
    {
    text: "text 4",
    color: "green"
    },
    {
    text: "text 5",
    color: "#f0f"
    }
];
const color = "green";
const ArrayTest = [1, 2, 3];
const ObjectTest = {"key": "value"};

class App extends React.Component{
    constructor(props){
        super(props);
        this.state  = {
            showSate: false
        };
    }
    setModalVisible = (value) => {
        console.log(`showSate`, this.state.showSate);
        console.log(`value`, value);
        this.setState({
            showSate: value
        });
        // 状态更新可能是异步的
        setTimeout(() => {
          console.log(`showSate`, this.state.showSate);
        });
    };
   XC = (e) => {
       let m = e.toString();
       console.log(e, m);
       return alert(`e.toString(); =\n`, m);
   };
  render(){
      return(
          <div>
               <div>
                   <button onClick={() => console.log(`smg`)}>
                       onClick
                   </button>
                   <button onClick={()=>this.setModalVisible(true)}>
                       showModal{this.state.showSate}
                   </button>
               </div>
               <MessageList
                   messages={text}
                   color={color}
                   arr={ArrayTest}
                   obj={ObjectTest}
                   xyzClick={this.XC}/>
          </div>
      );
    }
};



export default App;

ReactDOM.render(
    <App />,
    mountNode
);



/********************************* new version *************************************/

// props is an object!


// message.text => array of objects
class MessageList extends React.Component {
    getChildren = () => {
        //
    }
    render() {
        const children = this.props.messages.map(
            (message, index) => {
                //console.log(`message = `, message);
              console.log(`index = `, index);
                //console.log(`typeof index = `, typeof index);
                let xindex = 'id_' + index; 
                console.log(`xindex = `, xindex);
                //console.log(`typeof xindex = `, typeof xindex);
                return(
                    <Message
                        key={index}
                        indexkey={index}
                        text={message.text}
                        color={message.color}
                        xyzClick={this.props.xyzClick}
                    />
                );
            }
        );
        const styles = {
          css1: {
              color: 'red'
            },
            css2: {
              color: '#0f0'
            }
        }
       return(
            <div>
                children = {children}
                <hr />
                <div>
                    BAD: <br />
                    {/* this.props = {this.props} */}
                    <div style={styles.css1}>
                        this.props.children = {this.props.color}
                    </div>
                    {/* this.props.arr = {this.props.arr} */}
                    {/* this.props.obj = {this.props.obj} */}
                    <br />
                    <p style={styles.css2}>
                        Object Error, need using map items to item
                    </p>
                </div>
            </div>
        );
    }
}

// text={message.text} => message object
class Message extends React.Component {
    render() {
      //console.log(`this.props.text = `, this.props.text);
        //console.log(`this.props.key= `, this.props.key);
        let style = `
          color: red;
            font-size: 23px;
        `;
        if(this.props.key === undefined){
          //alert(`smg!`);
            console.log(`%c this.props.key= \n`, style, this.props.key);
        }
        return (
            <div>
                <hr />
                this.props.key = {this.props.key}
                <br />
                this.props.indexkey = {this.props.indexkey}
                <br />
                this.props.text = {this.props.text}
                <br />
                this.props.color = <span style={{backgroundColor: this.props.color}}>{this.props.color}</span>
                <br />
                <Button color={this.props.color} xyzClick={this.props.xyzClick}>
                    <span style={{color: '#fff'}}>Delete</span>
                </Button>
            </div>
        );
    }
}

// props.children === <span style={{color: '#fff'}}>Delete</span> ??? 
class Button extends React.Component {
    render() {
        return (
            <button
                style={{background: this.props.color}} 
                onClick={(e) => this.props.xyzClick(e)} 
                >
                {this.props.children}
            </button>
        );
    }
}




const text = [
    {
    text: "text 1",
    color: "red"
    },
    {
    text: "text 2",
    color: "blue"
    },
    {
    text: "text 3",
    color: "grey"
    },
    {
    text: "text 4",
    color: "green"
    },
    {
    text: "text 5",
    color: "#f0f"
    }
];
const color = "green";
const ArrayTest = [1, 2, 3];
const ObjectTest = {"key": "value"};

class App extends React.Component{
    constructor(props){
        super(props);
        this.state  = {
            showSate: false
        };
    }
    setModalVisible = (value) => {
        console.log(`showSate`, this.state.showSate);
        console.log(`value`, value);
        this.setState({
            showSate: value
        });
        // 状态更新可能是异步的
        setTimeout(() => {
          console.log(`showSate`, this.state.showSate);
        });
    };
   XC = (e) => {
       let m = e.toString();
       console.log(e, m);
       return alert(`e.toString(); =\n`, m);
   };
  render(){
      return(
          <div>
               <div>
                   <button onClick={() => console.log(`smg`)}>
                       onClick
                   </button>
                   <button onClick={()=>this.setModalVisible(true)}>
                       showModal{this.state.showSate}
                   </button>
               </div>
               <MessageList
                   messages={text}
                   color={color}
                   arr={ArrayTest}
                   obj={ObjectTest}
                   xyzClick={this.XC}/>
          </div>
      );
    }
};



export default App;

ReactDOM.render(
    <App />,
    mountNode
);
  • https://stackoverflow.com/questions/40448253/how-to-using-es6-arrow-function-to-realize-immediately-invoked-function-expressi?noredirect=1#comment78072726_40448253 – xgqfrms Aug 09 '17 at 15:19