简体   繁体   中英

ReactJS class component render array.map using contenteditable elements

I'm having an interesting issue that I cannot debug.

Goal

On a class component, inside of render function, iterate over an array of objects from state using this.state.items.map((item, index) => {}) and return a contentEditable paragraph element.

On each contentEditable paragraph element, listen for the onKeyUp event. If the key being used from e.which is the enter (13) key, add a new item to this.state.items using the index of the element that was keyed, in order to insert a new element after that index using splice .

Seeing Expected Result?

No. The newly added item is instead being put at the end of the loop when it is being rendered.

Example situation and steps to reproduce:

  1. Type "test1" into the first P element
  2. Hit enter (a new P element is created and focused)
  3. Type "test2" into this second, newly created, P element
  4. Refocus on the first P element, either by shift+tab or clicking
  5. Hit enter
  6. See observed results: a new P element is created and focused, but it is at the end of the list and not where it is intended to be, which is between the "test1" and "test2" P elements

Here is the code that I have so far:

class MyComponent extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            items: [this.paragraphTemplate()]
        }
    }

    render() {
        return (
            <section>
                <div>
                    {this.state.items.map((item, index) => {
                        return <p ref={item.ref} 
                                  key={index}
                                  contentEditable 
                                  suppressContentEditableWarning 
                                  onKeyUp={e => this.handleParagraphKeyUp(e, index, item)}></p>
                    })}
                </div>
            </section>
        )
    }

    handleParagraphKeyUp = (e, index, item) => {
        if (e.which === 13) {
            let addition = this.paragraphTemplate()

            this.setState(state => {
                state.items.splice(index + 1, 0, addition)

                return {
                    blocks: state.items
                }
            }, () => {
                addition.ref.current.focus()

                /* clear out the br and div elements that the browser might auto-add on "enter" from the element that was focused when the "enter" key was used */
                this.state.items[index].ref.current.innerHTML = this.state.items[index].ref.current.innerHTML.replace(/<br\s*[\/]?>/gi, '').replace(/<[\/]?div>/gi, '')
            })

            return false
        }
    }

    paragraphTemplate = () => {
        return {
            ref: React.createRef()
        }
    }
}

export default MyComponent

Here is a jsfiddle with the code from above.

If you take the above steps, you will see the issue that I am having.

Let me know if you require any further information, thanks in advance!

PS Please let me know if there any improvements that I can make to the code. I have been working in React for a short amount of time, and would love any feedback on how to make it better/cleaner.

UPDATED

Added key={index} to the P element. Note: this does not reflect any answers, it was merely added to stay in line with ReactJS list rendering.

to render a list of items, React needs key to keep track of the element see this: https://reactjs.org/docs/lists-and-keys.html

here is your updated fiddle that working..

<p ref={item.ref} 
   key={item.id}
   contentEditable 
   suppressContentEditableWarning 
   onKeyUp={e => this.handleParagraphKeyUp(e, 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM