0

I am using React JS and I have a text field which is supposed to change its content as the user clicks on different UI components. I also want to be able to edit the text in that text field (and later I would like to send that text to the UI component, but that is another story). So far I got this code

import React, { useContext } from 'react'
import './ContextualMenu.css'
import { EditorContext } from '../../EditorBase'

const ContextualMenu = props => {
  const editorContext = useContext(EditorContext)
  const handleUpdates = (event) => {
    console.log(event.target.value)
  }
  const displayNodeAttr = () => {
    return (
      <>
        <div className="menu-title">{editorContext.selectedNode.nodeType}</div>
        <div>
          <div className="menu-item">
            <div className="menu-item-label">Name</div>
            <div className="menu-item-value">
              <input
                className="menu-item-input"
                type="text"
                value={editorContext.selectedNode.nodeAttr.name}
                onChange={handleUpdates}
              />
            </div>
          </div>
        </div>
      </>
    )
  }

  return (
    <div id="c-contextual-menu">
      {editorContext.selectedNode.nodeAttr && displayNodeAttr()}
    </div>
  )
}

export default ContextualMenu

This makes the text always return to the original text that was set when the user clicked on the component. If i replace line 21 (value={editorContext.selectedNode.nodeAttr.name}) with placeholder={editorContext.selectedNode.nodeAttr.name} then the hint always shows the correct text as the user click on UI components but it shows it as a hint and i would like to have it as text. It seems to me that the input text field detects a change (has a listener on the change event or something like that) and it immediately reverts the text to the original text, which makes it basically uneditable. Any ideas?

Update: After the answers by @alireza and @Juviro I changed the code due to the fact that initially the selected node is null and as the user selects the node then it becomes not null. So the code now looks like this (it is just the relevant part):

const ContextualMenu = props => {
  const editorContext = useContext(EditorContext)
  const val = editorContext.selectedNode && editorContext.selectedNode.nodeAttr ? editorContext.selectedNode.nodeAttr.name : ''
  const [value, setValue] = useState(val)
  const handleUpdates = (event) => {
    setValue(event.target.value)
    console.log(event.target.value)
  }
  const displayNodeAttr = () => {
    return (
      <>
        <div className="menu-title">{editorContext.selectedNode.nodeType}</div>
        <div>
          <div className="menu-item">
            <div className="menu-item-label">Name</div>
            <div className="menu-item-value">
              <input
                className="menu-item-input"
                type="text"
                value={value}
                onChange={handleUpdates}
              />
            </div>
          </div>
        </div>
      </>
    )
  }

  return (
    <div id="c-contextual-menu">
      {editorContext.selectedNode.nodeAttr && displayNodeAttr()}
    </div>
  )
}

The problem now is that the input field is never set to any value when the user clicks on the UI components (nodes). It is as if the value is set on page load and then never updated as the user selects components (nodes). If now I use val instead of value like this: value={val} then the input field is updated correctly but then i get back to the old problem of not being able to edit its content.

3
  • Looking at your code, it's not clear where you store the input value. Typically, the input value is stored in state in onChange handler, but there you have only "console.log()" Commented Nov 14, 2019 at 15:05
  • Thank you @g_ain but yes, later the onChange event will implement the code that will send the value back to the component, but that is not the issue I am trying to fix right now. I am not sure i understand your question. The value is stored in the value property of the input component. Commented Nov 14, 2019 at 15:08
  • 1
    By adding the value prop to an input component you are setting it as a controlled component, meaning that you need to handle passing down the changed value after onChange event is triggered. Commented Nov 14, 2019 at 15:13

3 Answers 3

1

You can use the effect hook to call the setValue function when the value of val changes


import React, { useEffect, useState, useContext } from 'react'

const ContextualMenu = props => {
  const editorContext = useContext(EditorContext)
  const val = editorContext.selectedNode && editorContext.selectedNode.nodeAttr ? editorContext.selectedNode.nodeAttr.name : ''
  const [value, setValue] = useState(val)

  useEffect(() => {
    setValue(val)
  }, [val])

  const handleUpdates = (event) => {
    setValue(event.target.value)
    console.log(event.target.value)
  }
  const displayNodeAttr = () => {
    return (
      <>
        <div className="menu-title">{editorContext.selectedNode.nodeType}</div>
        <div>
          <div className="menu-item">
            <div className="menu-item-label">Name</div>
            <div className="menu-item-value">
              <input
                className="menu-item-input"
                type="text"
                value={value}
                onChange={handleUpdates}
              />
            </div>
          </div>
        </div>
      </>
    )
  }

  return (
    <div id="c-contextual-menu">
      {editorContext.selectedNode.nodeAttr && displayNodeAttr()}
    </div>
  )
}
Sign up to request clarification or add additional context in comments.

Comments

1

The solution from @alireza looks good, just replace

useState(event.target.value)

with

setValue(event.target.value)

Comments

1

This component always shows the same value as editorContext.selectedNode.nodeAttr.name you should use state in the component to handle it's value.

import React, { useContext, useState } from 'react'
import './ContextualMenu.css'
import { EditorContext } from '../../EditorBase'

const ContextualMenu = props => {
  const editorContext = useContext(EditorContext)
  const [value, setValue] = useState(editorContext.selectedNode.nodeAttr.name)
  const handleUpdates = (event) => {
    setValue(event.target.value)
    console.log(event.target.value)
  }
  const displayNodeAttr = () => {
    return (
      <>
        <div className="menu-title">{editorContext.selectedNode.nodeType}</div>
        <div>
          <div className="menu-item">
            <div className="menu-item-label">Name</div>
            <div className="menu-item-value">
              <input
                className="menu-item-input"
                type="text"
                value={value}
                onChange={handleUpdates}
              />
            </div>
          </div>
        </div>
      </>
    )
  }

  return (
    <div id="c-contextual-menu">
      {editorContext.selectedNode.nodeAttr && displayNodeAttr()}
    </div>
  )
}

I have not tested but it should work.

update To update the value based on the values coming from props, you should use useEffect.

import React, { useContext, useEffect, useState } from 'react'
import './ContextualMenu.css'
import { EditorContext } from '../../EditorBase'

const ContextualMenu = props => {
  const editorContext = useContext(EditorContext)
  const val = editorContext.selectedNode && editorContext.selectedNode.nodeAttr.name ? editorContext.selectedNode.nodeAttr.name : ''
  const [value, setValue] = useState(val)

  const handleUpdates = (event) => {
    setValue(event.target.value)
    console.log(event.target.value)
  }

  useEffect(()=>{
    if( val !== value) // Prevent redundant updates
      setValue(val)
  })

  const displayNodeAttr = () => {
    return (
      <>
        <div className="menu-title">{editorContext.selectedNode.nodeType}</div>
        <div>
          <div className="menu-item">
            <div className="menu-item-label">Name</div>
            <div className="menu-item-value">
              <input
                className="menu-item-input"
                type="text"
                value={value}
                onChange={handleUpdates}
              />
            </div>
          </div>
        </div>
      </>
    )
  }

  return (
    <div id="c-contextual-menu">
      {editorContext.selectedNode.nodeAttr && displayNodeAttr()}
    </div>
  )

3 Comments

@corneliu Sorry it was setValue instead of setState which @Juviro fixed
Yes, I have updated my code and i have edited my original post to show it. Can you please take a look again at it?
Alireza, I have tried your code but it gives the same result as last time. However @hbentlov's code does exactly what I wanted, so I accepted his answer. In any case, thank you very much for your help.

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.