skip to Main Content

I am having a trouble here, I created a choices wherein if the user clicked the Other's button, the TextField will be open, but the problem is, when I click the Others button again, it will not closed.

What I want here is keep the TextField Open, as long as the button is highlighted and close it when the user unclicked` it

In this image, I clicked it, so It will open

First Image

But when I clicked it again.

Second Image

It is still open.

So what I did here is I rendered the Question as well as the Choices, and check if the choices's essay === true, then show the TextField

{
    "_id": "642d892955e310c62e46b9d3",
    "title": "Question Sample",
    "choices": [
        {
            "choices": "GAD Orientation",
            "essay": false,
        },
        {
            "choices": "Gender Sensitivity Training",
            "essay": false,
        },
        {
            "choices": "Gender Mainstreaming",
            "essay": false,
        },
        {
            "choices": "GAD Planning and Budgeting",
            "essay": false,
        },
        {
            "choices": "GAD Analysis Tools",
            "essay": false,
        },
        {
            "choices": "Harmonized Gender and Development Guidelines",
            "essay": false,
        },
        {
            "choices": "Gender Mainstreaming Evaluation Framework",
            "essay": false,
        },
        {
            "choices": "Others, enumerate",
            "essay": true,
        },
        {
            "choices": "None",
            "essay": false,
        }
    ],
    "type_of_question": true,
}

So what I did here is, I checked the choice.essay === true then Open it.
Part4.js

  const [openChoiceId, setOpenChoiceId] = useState(null);
  const handleClick = (choice) => {
    if (choice.essay === true) {
      setOpenChoiceId(choice.choices);
    } else {
    }
  };


{currentChoices.map((choice, index) =>(
                  <Grid item xs={6} key={index}>
                   <label className="toggle-button" key={choice.id} onClick={() => handleClick(choice)}>
                   <input className="toggle-button__state" type="checkbox" name="choice" value={choice.choices} onChange={handleChange}/>
                   <span className="toggle-button__text">{choice.choices}</span>
                   
                   <Box sx={{position:'absolute', bottom: 50, left: 0, width: '100%', px: 2}}>
                   {openChoiceId === choice.choices && choice.essay === true && 
                   <TextField
                    id="filled-multiline-static"
                    label="Others"
                    fullWidth
                    multiline
                    rows={3}
                    name="essay" onChange={handleTextFieldChange} 
                    variant="outlined"
                  />}

                   </Box>

                 </label>
                  </Grid>
                  
              ))}

EDIT:

When I tried this.

const handleClick = (choice) => {
      setOpenChoiceId(choice.choices);
  };

Third image

Additional Info

I created a codesandbox here, I hope this one helps

https://codesandbox.io/s/pensive-star-wj57um?file=/src/sampleData.js

HandleChange comes from my parent component, so this is how it looks like

const [answer,setAnswer] = useState({
      email: email,
      category: type,
      affiliation: affiliation,
      part: `part${getSurveyPart}`,
      choice: "",
      essay: "",
     })

HandleChange.js

   const handleChange = (e) => {
            const value = e.target.value;
            const inputType = e.target.type;
        
            let newChoice = answer.choice; // initialize newChoice with the current choice array
        
            if (inputType === 'radio') {
            newChoice = [value];
            } else if (inputType === 'checkbox') {
            const isChecked = e.target.checked;
            const choiceIndex = answer.choice.indexOf(value);
            if (isChecked) {
                newChoice = [...answer.choice, value];
            } else {
                newChoice = [
                ...answer.choice.slice(0, choiceIndex),
                ...answer.choice.slice(choiceIndex + 1),
                ];
            }
            }
        
            const newAnswer = { ...answer, choice: newChoice };

            setAnswer(newAnswer);
        };

4

Answers


  1. From the edited code it looks like you are selecting multiple choices and when you select other you need to have the input shown up. You can do something like below to achieve this.

    const [openChoices, setOpenChoices] = useState([]);
      const handleChange = (e, choice) => {
        if (e.target.checked) {
          setOpenChoices(prev => [...prev, choice.choices]);
        } else {
          setOpenChoices(prev => prev.filter(p => p !== choice.choices));
        }
      };
    

    And in the JSX

    {currentChoices.map((choice, index) =>(
                      <Grid item xs={6} key={index}>
                       <label className="toggle-button" key={choice.id}>
                       <input className="toggle-button__state" type="checkbox" name="choice" value={choice.choices} onChange={e => handleChange(e, choice)}/>
                       <span className="toggle-button__text">{choice.choices}</span>
                       
                       <Box sx={{position:'absolute', bottom: 50, left: 0, width: '100%', px: 2}}>
                       {openChoices.includes(choice.choices) && choice.essay === true && 
                       <TextField
                        id="filled-multiline-static"
                        label="Others"
                        fullWidth
                        multiline
                        rows={3}
                        name="essay" onChange={handleTextFieldChange} 
                        variant="outlined"
                      />}
    
                       </Box>
    
                     </label>
                      </Grid>
                      
                  ))}
    
    Login or Signup to reply.
  2. I think you just need a simple boolean state and based on the selected choice you can toggle its value.

    Replace

    const [openChoiceId, setOpenChoiceId] = useState(null);
    
    const handleClick = (choice) => {
        if (choice.essay === true) {
          setOpenChoiceId(choice.choices);
        } else {
        }
      };
    

    With

    const [visibleTextField, setVisibleTextField] = useState(false);
    
    const handleChange = (e, choice) => {
      if (choice.essay) {
        setVisibleTextField(e.target.checked);
      }
    };
    

    and condition to show text field should be replaced with

    {
      visibleTextField && (
        <TextField
          id="filled-multiline-static"
          label="Others"
          fullWidth
          multiline
          rows={3}
          name="essay"
          onChange={handleTextFieldChange}
          variant="outlined"
        />
      );
    }
    

    Listen to change event instead of click like below

    <input
        className="toggle-button__state"
        type="checkbox"
        name="choice"
        value={choice.choices}
        onChange={(e) => handleChange(e, choice)}
      />
    

    Final code

    const [visibleTextField, setVisibleTextField] = useState(false);
    
    const handleChange = (e, choice) => {
      if (choice.essay) {
        setVisibleTextField(e.target.checked);
      }
    };
    {
      currentChoices.map((choice, index) => (
        <Grid item xs={6} key={index}>
          <label className="toggle-button" key={choice.id}>
            <input
              className="toggle-button__state"
              type="checkbox"
              name="choice"
              value={choice.choices}
              onChange={(e) => handleChange(e, choice)}
            />
            <span className="toggle-button__text">{choice.choices}</span>
          </label>
    
          <Box
            sx={{
              position: 'absolute',
              bottom: 50,
              left: 0,
              width: '100%',
              px: 2,
            }}
          >
            {visibleTextField && (
              <TextField
                id="filled-multiline-static"
                label="Others"
                fullWidth
                multiline
                rows={3}
                name="essay"
                variant="outlined"
              />
            )}
          </Box>
        </Grid>
      ));
    }
    
    Login or Signup to reply.
  3. When you click any of the buttons, make sure initially to set all "choices.essay" value to false. You can do it by:

    choices.forEach(item => item.essay = false);
    

    Then, you either open or close the essay textarea on button click. If the essay is true, you set to false. If essay value is false, you set to true.

    const handleClick = (choice) => {
        if(choice.essay === true) {
            choice.essay = false
        } else {
            choice.essay = true
        }
    }
    

    And, when you render component, make sure to render it by only when "choice.essay === true". You can remove "openChoiceId === choice.choice" statement. It goes like this

    {choice.essay === true && 
        <TextField 
            id="filled-multiline-static"
            label="Others"
            fullWidth
            multiline
            rows={3}
            name="essay" onChange={handleTextFieldChange} 
            variant="outlined"
        />}
           
    
    Login or Signup to reply.
  4. there are multiple thing you that needs to be fixed

    first you are not checking wether the checkbox is actually checked or not so you can either remove it or add it to the state

      onClick={(e) => handleClick(choice,e)}
    
      const handleClick = (choice,e) => {
        setOpenChoiceId(e.currentTarget.checked ? choice : {});
      };
    

    i removed the span with the text and instead added a label with an htmlFor
    so whenever you click on the text it clicks on the actual checkbox
    and right now the onClick event handler need to be on the checkbox not the label

        <label
          className="toggle-button"
          htmlFor={choice._id}
        >
          {choice.choices}
          </label>
    
          <input
            className="toggle-button__state"
            id={choice._id}
            type="checkbox"
            name="choice"
          onClick={(e) => handleClick(choice,e)}
            value={choice.choices}
          />
    

    last thing is that you are doing a wrong conditional statement

    to display the textField you need to be checking against the state and not what you were doing previously
    also the conditional chaining .? is because the openChoiceId will be set to an empty when the none of checkboxes are checked and will be an object and won’t have essay as a property

    {openChoiceId?.essay === true && (jsx goes here) }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search