9

i'm trying to create a ZIP code input that loads street, state and city values automatically in a Create form using React-Admin. How can I populate the inputs based on the onBlur event of the zip code input? The best result i achieved is the following scenario:

I created a custom component that has 4 Inputs: zip code (in my country is called CEP), street address, state and city. Then I added an onBlur event on the zip input and set the value on the inputs based on state attributes. Here is the code

class CustomAddressInput extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      cep : '',
      address : '',
      uf : '',
      city : '',
    }
    this.setAddress = this.setAddress.bind(this);
  }
  setAddress(e){
    if(e.target.value != undefined){
      endereco(e.target.value).then((result)=>{
        this.setState({
          cep: result.cep,
          address: result.logradouro,
          uf: result.uf,
          city: result.localidade
        });
      });
    }
  }

  render() {
    const { classes } = this.props;
    return (
      <TextInput label="CEP" source="cep" onBlur={(e) => this.setAddress(e)} defaultValue={this.state.cep} />
      <TextInput label="Endereco" source="address" defaultValue={this.state.address}/>
      <SelectInput label="Estado" source="state" choices={stateList} defaultValue={this.state.uf}/>
      <TextInput label="Cidade" source="city" defaultValue={this.state.city}/>
    );
  }
}
export default withStyles(styles)(CustomAddressInput);

And i'm using it on a Create

...
<Create {...props}>
  <SimpleForm>
    <TextInput label="Nome" source="name"/>
    <TextInput label="CPF/CNPJ" source="cpfcnpj"/>
    <TextInput label="Email" source="email"/>
    <TextInput label="Senha" source="password" type="password" />
    <TextInput label="Telefone" source="phone" type="tel"/>
    <CustomAddressInput/>
    <BooleanInput label="Pode criar outros usuários do sistema" source="canCreateUser" defaultValue={false}/>
    <BooleanInput label="Pode gerenciar projetos" source="canCreateProjects" defaultValue={false}/>
    <BooleanInput label="Pode visualizar honorários" source="canSeeFees" defaultValue={false}/>
  </SimpleForm>
</Create>
...

I know i'm setting the values in a wrong way because when the values are set, all the create form is wiped. What should i do? I'm not familiar developing with React. Thanks in advance

2 Answers 2

9

I think i found the proper way of doing this. I moved the auto fill address function to a onChange event on the SimpleForm element and removed it from the CEP input. It works like a charm now. Here is the code:

Custom Address input

export default withStyles(styles)(
  class CustomAddressInput extends React.Component {
    render() {
      return (
        <div>
          <div>
            <TextInput label="CEP" source="cep" parse={parseCep} format={parseCep} validate={validateCEP}/>
          </div>
          <div>
            <TextInput label="Endereco" source="address"/>
            <SelectInput label="Estado" source="state" choices={stateList}/>
            <TextInput label="Cidade" source="city"/>
          </div>
        </div>
      );
    }
  }
);

And the Create Component

const autoFillAddress = (event)=>{
  if(event.cep){
    if(event.cep.length === 9){
      endereco(event.cep).then((result)=>{
        event.address = result.logradouro;
        event.state = result.uf;
        event.city = result.localidade;
      });
    }
  }
}
...
<Create {...props}>
  <SimpleForm onChange={autoFillAddress}>
    <div>
      <TextInput label="Nome" source="name" validate={validateName}/>
      <TextInput label="CPF/CNPJ" source="cpfcnpj" parse={parseCpfCnpj} format={parseCpfCnpj} validate={validateCpfCnpj}/>
    </div>
    <div className={classes.packTres, classes.fullInput}>
      <TextInput label="Email" source="email"validate={validateEmail}/>
      <TextInput label="Senha" source="password" type="password" validate={validatePassword}/>
    </div>
    <TextInput label="Telefone" source="phone" type="tel" parse={parsePhone} format={parsePhone} validate={validatePhone}/>
    <CustomAddressInput />
    <BooleanInput label="Pode criar outros usuários do sistema" source="canCreateUser" defaultValue={false}/>
    <BooleanInput label="Pode gerenciar projetos" source="canCreateProjects" defaultValue={false}/>
    <BooleanInput label="Pode visualizar honorários" source="canSeeFees" defaultValue={false}/>
  </SimpleForm>
</Create>
...
Sign up to request clarification or add additional context in comments.

1 Comment

This works for me on a create page in React-Admin as well. Now I'm trying to figure out how to do the same on an edit page, as this approach only seems to work on a Create.
0

After spending more hours than i would like trying to achieve this, decided to share my findings. This is currently working along with react-hook-form.

  1. Make sure you create a new component for the zip field like ZIPComponent bellow.
  2. Inside custom components you can use the useFormContext from react-hook-form to correctly interact with the form state and methods. Along with setValue you can use getValues, reset and others to manipulate any react-admin field.
  3. You can then use this ZIPComponent inside both <Create> and <Edit> react-admin pages where you would place the zip code field

import { useFormContext } from "react-hook-form";
import axios from "axios";
import { TextInput } from "react-admin";
export const ZIPComponent = () => {
  const { setValue } = useFormContext();

  const fetchZip = async (cep: any) => {
    try {
      const response = await axios.get(`http://viacep.com.br/ws/${cep}/json`);

      const { data } = response;

      setValue("addressZipCode", cep);
      setValue("addressState", data.uf);
      setValue("addressCity", data.localidade);
      setValue("addressNeighborhood", data.bairro);
      setValue("address", data.logradouro);
      return { ...data };
    } catch (error) {
      console.error("Erro ao buscar CEP: ", error);
      return null;
    }
  };

  return (
    <TextInput
      source="addressZipCode"
      label="CEP"
      fullWidth
      onChange={(e) => {
        if (e.target.value.length >= 7) fetchZip(e.target.value);
      }}
      format={(v: any) => formatZipCode(v)}
    />
  );
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.