Mi problema es que estoy tratando de manejar el valor de mis entradas, que el usuario define qué entrada quiere, mediante una llamada a la API.
Aquí es donde obtengo los valores:
const handleClick = buttonTitle => async () => { await renderField(buttonTitle).then(response => { navigation.navigate('FormScreen', { collectionKey: buttonTitle.slice(7), paramKey: JSON.stringify(response), }); }); }; El campo Render es una llamada API, que me devuelve {"message": [{"_id": "618e4c23db08f70b719f3655", "author": "adicionarei posteriormente", "ceatedAt": "2021-11-12 08:12:32", "field": "abc", "fieldtype": "Text"}, {"_id": "618e4c9ddb08f70b719fae37", "author": "adicionarei posteriormente", "ceatedAt": "2021-11-12 08:14:35", "field": "Animal", "fieldtype": "Text"}]}
Luego tengo mi componente Formulario, donde obtengo algunos componentes necesarios y los muestro para el usuario:
const FormScreen = ({route, navigation}) => { return ( <Container> <InputBody route={route.params.paramKey} navigation={navigation} /> </Container> // => handle submit Input it in here ? ); }; Para mi componente inputbody tengo el siguiente código (recordando que ( body.map es la respuesta de llamada api):
return ( <> {Object.keys(Body).length > 0 ? ( Body.map(item => ( <React.Fragment key={uuid.v4()}><Texto>{item.field}</Texto> {renderContent(item.fieldtype,item.field)} </React.Fragment> )) ) : ( <ActivityIndicator size="large" color="#eb6b09" /> )} </> ) } Luego tengo mi renderContent (donde obtengo el tipo de campo como una string y el nombre del campo que también es una string ).
function renderContent(type,field) { switch(type) { case 'Numeric': return <NumberInput key={field} keyboardType="numeric" /> case 'Text': return <TextInput key={field} /> } } Recordando que: cada tipo de campo puede aparecer más de una vez. (Por ejemplo: puedo tener un formulario con más de 1 entrada de texto), entonces mi pregunta es: ¿cómo puedo manejar los valores de mi entrada sabiendo que puede tener cualquier tipo de entrada ( Numeric or Text )?
obs: Puedo mostrar cualquier tipo de información.
const Input = ({value,keyboardType,onChange})=>{ return( <TextInput value={value} keyboardType={keyboardType} onChangeText={onChange} /> ) } const [payload,setPayload] = useState({}); const onValue=(e,field)=>{ let tempPayload = {...payload}; tempPayload[field] = e; setPayload(tempPayload) } const renderComponent = (fieldObj)=>{ switch(fieldObj.type): case "Text": return <Input keyboardType="default" onChange={(e)=>onValue(e,fieldObj.field)} value={payload[fieldObj.field]||""}/> case "Number": return <Input keyboardType="numeric" onChange={(e)=>onValue(e,fieldObj.field)} value={payload[fieldObj.field]||""} /> case "Dropdown": return <Dropdown options={fieldObj.options} /> //if you want to add dropdown, radio buttons etc in future } La idea es bastante sencilla. Almacene los valores de los campos de formulario en una payload de objeto. El nombre es el nombre del campo, por ejemplo. Animal. El valor es el valor de ese campo. También puede inicializar el objeto con todas las claves y sus valores como vacío o un valor predeterminado que obtiene de la API. Entonces, si los campos que hemos renderizado son Animal y Car. La carga útil será
{ 'Animal':'Tiger', 'Car':'BMW' }Esto se maneja usando la función onValue. También puede agregar validación en esta función. Por ejemplo, si pasa una expresión regular con su API para ese campo, puede validar el valor usando la expresión regular.
Fue un poco complicado, así que lo simplifiqué, creo que deberías entender la lógica detrás de esto.
import React, { useState } from 'react'; import { TextInput } from 'react-native'; const createInitialState = (inputList) => { return inputList.reduce((accumulator, currentValue) => { return { ...accumulator, [currentValue.field]: '', }; }, {}); }; const SomeScreen = () => { const initialDataPassed = [ { '_id': '618e4c23db08f70b719f3655', 'author': 'adicionarei posteriormente', 'ceatedAt': '2021-11-12 08:12:32', 'field': 'abc', 'fieldType': 'Text', }, { '_id': '618e4c9ddb08f70b719fae37', 'author': 'adicionarei posteriormente', 'ceatedAt': '2021-11-12 08:14:35', 'field': 'Animal', 'fieldType': 'Text', }, { '_id': '618e4c9ddb08f70b719fae37', 'author': 'adicionarei posteriormente', 'ceatedAt': '2021-11-12 08:14:35', 'field': 'Animal', 'fieldType': 'Number', }, ]; return ( <Form inputList={initialDataPassed} /> ); }; const Form = ({ inputList }) => { const [formState, setFormState] = useState(createInitialState(inputList)); return ( <> {inputList.map((item) => { const handleTextInputValueChange = (text) => { // this is solution is better if we base on old value setFormState(oldState => ({ ...oldState, [item.field]: text })) }; return <Input key={item.field} value={formState[item.field]} onChangeText={handleTextInputValueChange} fieldType={item.fieldType} /> })} </> ); }; const Input = ({value, onChangeText, fieldType}) => { const keyboardType = fieldType === 'Number' ? 'numeric' : undefined; return <TextInput value={value} keyboardType={keyboardType} onChangeText={onChangeText} /> };Si solo desea este tipo de entrada , puede hacerlo de esta manera:
Primero, defina y objete para mapear sus tipos de campo a tipos de entrada html:
const inputTypesMapper = { Numeric: "number", Text: "text", Boolean: "checkbox" };Y así puedes representarlos de la siguiente manera:
<div className="App"> {data.message.map(({ fieldtype, field }) => ( <input type={inputTypesMapper[fieldtype]} defaultValue={field} /> ))} </div>Pero si desea representar diferentes componentes para cada tipo de campo, puede hacer lo siguiente:
Primero, defina y objete para mapear sus tipos de campo a tipos de entrada html:
const inputTypesMapper = { Text: ({ value }) => { return <input type={"text"} defaultValue={value} />; }, MultipleOptions: ({ value }) => { return ( <select> {value.map(({ id, value }) => { return <option value={id}>{value}</option>; })} </select> ); } };Y así puedes representarlos de la siguiente manera:
return ( <div className="App"> {data.message.map(({ fieldtype, field }) => { const renderInput = inputTypesMapper[fieldtype]; return renderInput({ value: field }); })} </div> );