Defina o valor do botão de opção em React
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 defaultData
e atribuir seu respectivo ContactMode
modo 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 useEffect
gancho 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 Expand
botão abaixo do primeiro.
-> Ao clicar no save
botã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
Em sua caixa de areia, consegui fazer com que os botões de opção funcionassem
- Fornecendo um único
name
para cada grupo de rádio mapeado - Atualize o manipulador de alterações para manipular exclusivamente a
ContactMode
propriedade.
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 handleInputChange
adicione um caso para tratar ContactMode
especialmente.
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 };
});
};
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 ===
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
Aqui estão alguns problemas em seu código ...
Sempre que o componente retorna, o
.map
é chamadodefaultData
. Portanto, não mapeado para o estado.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.É 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>