0

NOTE: this question aims to understand why this code gives this behaviour, not what alternative code would give the desired one.

I have a custom component to represent a particular form field, and I wanted to conditionally render it with different props depending on conditions. In the real example, more props change between the cases, but I have distilled it to this minimal example below, see this fiddle:

https://jsfiddle.net/xkzywjvL/1/

function DummyInput({defaultValue}) {
console.log(defaultValue)
    return (
  <input defaultValue={defaultValue}/>
  )
}

function Picker({setContentOption})
{
  const handleOptionChange = (event) => {
    setContentOption(event.target.value);
  };

  return (
    <div>
      
      <select onChange={handleOptionChange}>
        <option value="content-A">content-A</option>
        <option value="content-B">content-B</option>
        <option value="content-C">content-C</option>
      </select>
      </div>
      )
}

function App() {
const [contentOption, setContentOption] = React.useState('content-A')
let inputField = null

if (contentOption === 'content-A'){
    inputField = <DummyInput defaultValue="A"></DummyInput>
}
else if (contentOption === 'content-B'){
    inputField = <DummyInput defaultValue="B"></DummyInput>
}
else if (contentOption === 'content-C'){
    inputField = <DummyInput defaultValue="C"></DummyInput>
}

return (
    <div>
      <div>See that the console prints three times, meaning that
      code is executed in all three versions, but only the third
      re-renders the component</div>
      <br/>
      <Picker setContentOption={setContentOption}/>
      <br/>
      <div>Does not work - if / else statement in function body</div>
      {inputField}
      
      <div>Does not work - inline if else statement</div>
      {(contentOption === 'content-A') && (
      <DummyInput defaultValue="A"></DummyInput>
      ) || (contentOption === 'content-B') && (
      <DummyInput defaultValue="B"></DummyInput>
      ) || (contentOption === 'content-C') && (<DummyInput defaultValue="C"></DummyInput>)}
      
      <div>Works - three separate inline if statements</div>
      {contentOption === 'content-A' && <DummyInput defaultValue="A"></DummyInput>}
      {contentOption === 'content-B' && <DummyInput defaultValue="B"></DummyInput>}    
      {contentOption === 'content-C' && <DummyInput defaultValue="C"></DummyInput>}
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector("#app"))

The app renders a form field component DummyInput with a defaultValue that changes depending on state contentOption from the parent App component (in the real example there are more differences that one prop). I expected the defaultValue to change based on the state, regardless of the conditional rendering method used.

I originally assigned the component to a variable inputField in an if - else statement, but realised that it did not update the component. Instead, if I use three separate && to render conditionally, the components do update as in the example, but not if I use an inline if-else.

By putting a console.log statement in DummyInput I can see that even though the component is not re-rendering with the updated defaultValue, the code inside the component is being executed.

What is the reason for this behaviour? The reason why I want to know is that I might be inadvertedly doing something similar elsewhere without noticing.

Similar previous questions

This question is similar to this one and this one, and if I add a "key" to the DummyInput, it works. However, I don't understand why this is needed outside of a map function, nor why three separate inline if statements work.

1 Answer 1

0

The defaultValue is changing in all cases. You don't see it because once the input is rendered the first time it now has a value, and changing the defaultValue after that does not change the value of that input. It works for 3rd case because in that case it is destroying the component and creating a new one everytime.

If you type something in the inputs and then change the dropdown, you'll see that the first two values remain, the third one loses your input.

It should become apparent if you change to placeholder. <input placeholder={defaultValue}/>

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! that makes sense, and it only happens for the inputValue, and not other properties. However, I still don't fully understand why React "thinks" that the component is the same with the regular if / else, but not with the three separate inline statements. It's not the DOM position, since it's the same for all three implementations. I see what you mean by the destroying of the componet, but I would have never thought this would be different from a regular if / else. Do you know if other html attributes have similar behaviours?
It is the same component, your code is compiled by react into js, and since it's all within the same {} it deals with it "smartly" when compiling. The defaultValue has a side effect in a way that it sets the value, if a value is not provided.

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.