Defina o valor do botão de opção em React

Nov 24 2020

Estou fazendo um aplicativo de reação simples com formulário que possui botões de opção.

Aqui há dados padrão disponíveis como,

const defaultData = [{ ContactMode: 3 }, { ContactMode: 2 }, { ContactMode: 2 }];

Requerimento:

-> Precisa iterar isso defaultDatae atribuir seu respectivo ContactModemodo conforme verificado em cada linha.

Snippet de trabalho:

const { useState } = React;

const App = () => {
  const [formValue, setFormValue] = useState({
    ContactMode: 1
  });

  const defaultData = [{ ContactMode: 3 }, { ContactMode: 2 }, { ContactMode: 2 }];

  const handleInputChange = (e, index) => {
    const { name, value } = event.target;
    setFormValue({
      ...formValue,
      [name]: value
    });
  };

  const submitData = () => {
    console.log(formValue);
  };

  return (
    <div>
      <form>
        {defaultData.map((item, index) => {
          return (<React.Fragment>
            <label> Contact Mode </label>
            <input
              type="radio"
              name="ContactMode"
              checked={item.ContactMode == 1}
              value={1}
              onChange={(event) => handleInputChange(index, event)}
            />{" "}
            Email
            <input
              type="radio"
              name="ContactMode"
              checked={item.ContactMode == 2}
              value={2}
              onChange={(event) => handleInputChange(index, event)}
            />{" "}
            Text
            <input
              type="radio"
              name="ContactMode"
              checked={item.ContactMode == 3}
              value={3}
              onChange={(event) => handleInputChange(index, event)}
            />{" "}
            Both
            <br />
            <br />
            <button type="button">
               Save
             </button>
            <br />
            <br />
            <br />
          </React.Fragment>);
        })}
      </form>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Saída esperada:

Contact Mode  Email Text Both (Checked)

Contact Mode  Email Text(Checked) Both

Contact Mode  Email Text(Checked) Both

Observação: não posso modificar o atributo de nome name="ContactMode". A razão é que também tenho um botão individual para salvar cada linha e, ao clicar nesse botão, essa linha específica será salva. E cada linha deve ter name="ContactMode"..

Editar:

Como minha pergunta não é clara sobre o que estou tentando alcançar, vou fornecer o código completo aqui.

-> Eu tenho um formulário escalonado em que a Etapa 2 é a seção de empregos e, por padrão, tenho os valores para definir no formulário,

const dynamicData = [
  {
    EmploymentID: 1,
    companyName: 'Company One',
    designation: 'Designation One',
    ContactMode: 3,
  },
  {
    EmploymentID: 2,
    companyName: 'Company two',
    designation: 'Designation One',
    ContactMode: 2,
  },
];

-> Dentro do useEffectgancho eu defino o valor do formulário como,

  React.useEffect(() => {
    setExpanded(0);
    setValue((prev) => {
      const companyDetails = [...dynamicData];
      return { ...prev, companyDetails };
    });
  }, []);

Aqui, os valores do formulário com caixas de entrada são vinculados corretamente, mas o valor do botão de opção não é verificado.

-> Existem dois dados disponíveis, mas o primeiro será expandido por padrão e o segundo será aberto ao clicar no Expandbotão abaixo do primeiro.

-> Ao clicar no savebotão, esse objeto específico ( inputField) será registrado no console (somente teste) junto com os dados modificados.

Aqui, o PROBLEMA é que, se modificarmos os dados, as coisas estão funcionando bem, mas o atributo de verificação do rádio sozinho não faz com que o item seja verificado .. (A indicação de verificado não está funcionando) ..

Vá para a etapa 2 na caixa de códigos fornecida abaixo para ver o problema. Para ver o segundo item, clique no botão de expansão.

Por favor, me ajude a definir o valor do botão de opção padrão em cada linha.

Respostas

1 DrewReese Nov 25 2020 at 14:15

Em sua caixa de areia, consegui fazer com que os botões de opção funcionassem

  1. Fornecendo um único namepara cada grupo de rádio mapeado
  2. Atualize o manipulador de alterações para manipular exclusivamente a ContactModepropriedade.

Atualize os grupos de rádios para usar o índice mapeado atual a fim de torná-los únicos entre todos os grupos mapeados.

<div className="form-group col-sm-4">
  <label className="inline-flex items-center mr-6">
    <input
      type="radio"
      className="form-radio border-black"
      name={`ContactMode-${index}`} // <-- append index to name value={1} checked={inputField.ContactMode === 1} // <-- use strict "===" onChange={(event) => handleInputChange(index, event)} /> <span className="ml-2">Email</span> </label> </div> <div className="form-group col-sm-4"> <label className="inline-flex items-center mr-6"> <input type="radio" className="form-radio border-black" name={`ContactMode-${index}`}
      value={2}
      checked={inputField.ContactMode === 2}
      onChange={(event) => handleInputChange(index, event)}
    />
    <span className="ml-2">Text</span>
  </label>
</div>
<div className="form-group col-sm-4">
  <label className="inline-flex items-center mr-6">
    <input
      type="radio"
      className="form-radio border-black"
      name={`ContactMode-${index}`}
      value={3}
      checked={inputField.ContactMode === 3}
      onChange={(event) => handleInputChange(index, event)}
    />
    <span className="ml-2">Both</span>
  </label>
</div>

No handleInputChangeadicione um caso para tratar ContactModeespecialmente.

const handleInputChange = (index, event) => {
  const { name, value } = event.target;
  if (name === 'designation' && !isLettersOnly(value)) {
    return;
  }

  if (name.startsWith('ContactMode')) { // <-- check name prefix
    setValue((prev) => ({
      ...prev,
      companyDetails: prev.companyDetails.map((detail, i) =>
        i === index
          ? {
              ...detail,
              ContactMode: Number(value), // <-- store back under correct key, as number for string equality check
            }
          : detail,
      ),
    }));
    return; // <-- return early so other state update is skipped!
  }

  setValue((prev) => {
    const companyDetails = prev.companyDetails.map((v, i) => {
      if (i !== index) {
        return v;
      }

      return { ...v, [name]: value };
    });

    return { ...prev, companyDetails };
  });
};

1 TalOrlanczyk Nov 24 2020 at 22:10

Acho que devido ao fato de todas as rádios terem o mesmo atributo de nome, cada grupo deve ter nome diferente. por exemplo:

name = " ex1"
name = "ex2"
name = "ex3"

para cada novo nome devido ao grupo como este. se você fizer o mesmo nome, tudo bagunçado, outro pensar que você é você ao ==invés de===

item.ContactMode == 3

é melhor usar ===

vkvkvk Nov 24 2020 at 22:49

A resposta de @TalOrlanczyk está certa. Você está imprimindo 9 botões de opção no total e todos têm o mesmo valor no atributo de nome. Então, tecnicamente, é como ter um grupo de rádio com 9 botões de opção, o que significa que um grupo de rádio só pode aceitar um valor. Executei seu código e selecionei apenas "Texto" na 3ª linha.

Se você está tentando ter 3 linhas separadas (grupos de rádio), você precisa tentar algo assim:

<input
    type="radio"
    name={"ContactMode"+index}
    checked={item.ContactMode == 1}
    value={1}
    onChange={(event) => handleInputChange(index, event)}
/>{" "}
Email
<input
    type="radio"
    name={"ContactMode"+index}
    checked={item.ContactMode == 2}
    value={2}
    onChange={(event) => handleInputChange(index, event)}
/>{" "}
Text
<input
    type="radio"
    name={"ContactMode"+index}
    checked={item.ContactMode == 3}
    value={3}
    onChange={(event) => handleInputChange(index, event)}
/>{" "}

Experimente e execute novamente o seu código

NeetigyaChahar Nov 25 2020 at 13:16

Aqui estão alguns problemas em seu código ...

  1. Sempre que o componente retorna, o .mapé chamado defaultData. Portanto, não mapeado para o estado.

  2. Se você quiser o mesmo nome para cada iteração, precisará usar <form>para cada iteração, caso contrário, todos os botões de opção serão agrupados, portanto, apenas o último botão de opção será exibido como marcado.

  3. É melhor usar em ===vez de==

Eu modifiquei seu código e defaultDataé usado para inicializar o estado.

const { useState } = React;


  const defaultData = [{ ContactMode: 3 }, { ContactMode: 2 }, { ContactMode: 2 }];

const App = () => {

  const [formValue, setFormValue] = useState([...defaultData]);

  const handleInputChange = (index, e) => {
    const { name, value } = e.target;

    const newFormValue = [...formValue];
    newFormValue.splice(index,1,{[name]: value});
    
    setFormValue(newFormValue);
  };

  const submitData = () => {
    console.log(formValue);
  };

  return (
    <div>
        {formValue.map((item, index) => {
          return (<form>
            <label> Contact Mode </label>
            <input
              type="radio"
              name="ContactMode"
              checked={Number(item.ContactMode) === 1}
              value={1}
              onChange={(event) => handleInputChange(index, event)}
            />{" "}
            Email
            <input
              type="radio"
              name="ContactMode"
              checked={Number(item.ContactMode) === 2}
              value={2}
              onChange={(event) => handleInputChange(index, event)}
            />{" "}
            Text
            <input
              type="radio"
              name="ContactMode"
              checked={Number(item.ContactMode) === 3}
              value={3}
              onChange={(event) => handleInputChange(index, event)}
            />{" "}
            Both
            <br />
            <br />
          </form>);
        })}
        <button type="button" onClick={submitData}>
          {" "}
          Submit{" "}
        </button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>