klye_g
klye_g

Reputation: 1242

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!

P.S. 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.

Upvotes: 0

Views: 4530

Answers (1)

Tubc
Tubc

Reputation: 416

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, 

Upvotes: 2

Related Questions