1
votes

I am having trouble making a checkbox react to its onChange event(checkbox remains in its default state). I have seen similar post here, but nothing really seems to work. I'm hoping a different set of eyes could help figure out whats wrong with my logic.

Here is the handleChange function, and the initial values of the input fields which are both in the parent component

const [accountData, setAccountData]=useState({accountType:'Free',  //default input value for text input
                                              accountStatus: false }) //default input for checkbox input

const handleSubmit={/*submitLogic*/}       

const handleChange = e =>{
         
const target = e.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
setAccountData({...accountData, [name]:value})
}

//passing the handle change as a prop to the EditView component\\
<EditView  handleChange={handleChange} handleSubmit={handleSubmit} data={accountData}/>

Here is the child component that contains the input form(Concern is the checkbox, as the text input field reacts to its onChange event).

const EditView = ({ handleChange, handleSubmit, data}) =>{
    
const isChecked=data.accountStatus;

<form onSubmit={handleSubmit}>
        <div className="form-row" >
                    <div className="form-group col-md-6">
                     < label htmlFor="accountType">Account Type:</label>
                       <input 
                           type="text"
                           className="form-control" 
                           id="accountType"
                           name="accountType"
                           value={data.accountType}
                            onChange={handleChange}/>
                           
                  </div>
                  <div className="form-group col-md-6">
                      < label htmlFor="accountStatus">Account Status:</label>
                      <div className="custom-control custom-switch">
                        <input 
                            type="checkbox"
                            className="custom-control-input "
                             id="accountStatus"
                             name='accountStatus'
                            checked={isChecked}
                            //value={isChecked}
                            onChange={handleChange}
                            
                            />
                  </div>
             </div>
       <button type='submit' >Submit</button>

</form>
}

I even tried creating a different onChange handle function, but still had the same problem. Have I done something wrong in the above code that makes my checkbox not change from its default state?

2
Not part of your question but elements with a space before the element name like < label htmlFor will not render properly - Mark Schultheiss

2 Answers

0
votes

The value passed to useState is the initial value of the state. If you want to use a values passed down through the properties, without managing it in the component itself don't use state.

const EditView = ({handleChange, handleSubmit, data}) => {
  const isChecked = data.accountStatus;

  // ...
};

When you use:

const [isChecked, setIsChecked] = useState(data.accountStatus);

You are saying, I want to manage isChecked in this component, but the initial value is set to data.accountStatus. For this reason changing data.accountStatus later on won't effect the value of isChecked, since that can only be changed with the setIsChecked function.


The reason the checkbox doesn't work after the edit you made to the question is due to the use of Bootstrap. When you use custom form elements Bootstrap will replace the actual input element, with some visual representation (styled element). This means that when you click on the Bootstrap checkbox/switch you aren't actually clicking on the input.

To solve this issue I'd recommend using React Bootstrap which integrates Bootstrap into React. Here is a working example (click the "full page" button after running the snippet to prevent overlapping of the console output with the content):

const {useState} = React;
const {render} = ReactDOM;
const {Form} = ReactBootstrap;

function App() {
  const [accountData, setAccountData] = useState({
    accountType: "Free",
    accountStatus: false,
  });
  
  const handleChange = ({target}) => {
    const name  = target.name;
    const value = target.type == "checkbox" ? target.checked : target.value;
    setAccountData({...accountData, [name]: value});
  };
  
  console.log(accountData);
  return <EditView data={accountData} onChange={handleChange} />;
}

function EditView({data, onChange}) {
  return (
    <Form>
      <Form.Group>
        <Form.Label>Account Type:</Form.Label>
        <Form.Control
          id="accountType"
          name="accountType"
          type="text"
          value={data.accountType}
          onChange={onChange}
        />
      </Form.Group>
      <Form.Group>
        <Form.Label>Account Status:</Form.Label>
        <Form.Switch
          id="accountStatus"
          name="accountStatus"
          label=""
          checked={data.accountStatus}
          onChange={onChange}
        />
      </Form.Group>
    </Form>
  );
}

render(<App />, document.getElementById("root"));
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" />
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js"></script>
<div id="root"></div>
0
votes

try this onChange={event => handleChange(event)}