0

I am building a simple React App to input: a title, a list of sentences (number depending on the user).

This is my App.js code:

import logo from './logo.svg';
import './App.css';
import {useState} from 'react';
// Import the functions you need from the SDKs you need
import { getFirestore, getDocs, collection, addDoc, deleteDoc, doc } from 'firebase/firestore'
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional

function App() {
  let total = 0;
  let [heading, setHead] = useState('');
  let [anslst, setAnslst] = useState(['']);
  let [swtch, setSwitch] = useState(true);

  function Headingbar(){
    return(
      <div className="head-rect padd">
        <div className="heading-div">
          <div className="side-text">FILL</div>
          <label className="switch">
            <input className="inputbox" type="checkbox" checked={!swtch} onChange={(e) => { setSwitch(!e.target.checked); }}/>
              <span className="slider round"></span>
          </label>
          <div className="side-text">VIEW</div>
        </div>
      </div>
    )
  }

  function Fill() {
    return (
      <>
        <form className="padd title-form">
          <input type="text" className="title-textbox textbox-defaults" placeholder="Title" onChange={(e) => { setHead(e.target.value) }} />
        </form>
        {
          anslst.map((i)=>{
            total += 1;
            return(
              <form className="padd answer-form">
                <textarea rows="10" cols="60" type="text" id={total - 1} className="answer-textbox textbox-defaults" placeholder="points" onChange={(e) => {
                  anslst[e.target.id] = e.target.value;
                  setAnslst(anslst);
                }} />
              </form>
            );
        })
        }
        <div className="bottom padd">
          <form>
            <input type="button" value="+" className="padd plus-button" onClick={()=>{
              anslst = [...anslst, ''];
              setAnslst(anslst);
              console.log(anslst);
            }}/>
          </form>
          <form>
            <input type="button" value="Submit" onClick={()=>{add(heading, anslst)}}/>
          </form>
        </div>
      </>
    )
  }

  return (
    <div className="App">
      <Headingbar />
      {swtch && <Fill />}
    </div>
  );
}

export default App;

The main piece of code in focus is:

<form className="padd title-form">
          <input type="text" className="title-textbox textbox-defaults" placeholder="Title" onChange={(e) => { setHead(e.target.value) }} />
</form>

This form seems to malfunction ie on entering values into it , the cursor automatically disappears from the form. Sometimes randomly one of the letters is filled into the form before the cursor disappears.

I expected the form to take any input I give to it.

My debugging attempts:

  1. When I remove the 'onChange' part of the input tag, it works as expected.
  2. When I replace the function inside onChange with any function which doesn't use setHead function, the form works as expected.

This convinced me that the problem is with setHead() but I couldn't understand what.

  • You are re-creating the `Fill` component on each render, move it out of the `App` component, and pass the values/function via props. – Ori Drori Mar 07 '23 at 17:37
  • Or https://stackoverflow.com/questions/68999032/creating-react-functional-components-inside-other-components-or-in-a-separate-fi – Brian Thompson Mar 07 '23 at 17:38
  • https://stackoverflow.com/questions/59776799/its-okay-to-declare-other-components-inside-a-component – Brian Thompson Mar 07 '23 at 17:38
  • Basically, the entire component is being unmounted and re-mounted, which is causing the focus to be lost and input to be unstable. The input is unstable because there is no `value` prop to hold the value between lifecycles. However the first problem is the more important – Brian Thompson Mar 07 '23 at 17:39
  • Thanks, guys the comments were helpful. Fixed it. – CaptainAmerica Mar 08 '23 at 04:30

1 Answers1

0

Well, ReactJS is a one-way data binding, so when you want to control the value yourself, you need to pass the attribute value for the input to make it controlled form field. Otherwise, the value you set (by setHead()) and the value it displayed is not matched (that's the malfunction you said).

That's why when you remove setHead(), it worked as expected (because the <input/> handle value itself, so the value displayed and the value of the input is the same one).

To fixed this, just add value for the <input/>.

<form className="padd title-form">
  <input type="text"
    className="title-textbox textbox-defaults"
    placeholder="Title"
    value={heading}
    onChange={(e) => { setHead(e.target.value) }}
  />
</form>

Additional: What is one-way data binding?

In one-way binding, the data flow is one-directional. It means that: at a time, only one of the following conditions can be followed:

  • Component to View: Any change in component data would get reflected in the view.
  • View to Component: Any change in view would get reflected in the component’s data.

Here in your case, the component data is your state (heading) (because you call setHead() inside onChange() function), while there's nothing to display in view now (because the <input/> missed the value attribute).

You can refer to this post for more explanations about one-way binding & two-way binding, as it not really related to your question's topic.

haptn
  • 650
  • 6
  • 11