2 votes

React Noob - L'élément onChange perd sa focalisation

J'essaie de créer une page de connexion basique qui accepte les informations d'identification de l'utilisateur et les soumet à une api de connexion. le problème est que lorsque l'api de connexion est activée, l'utilisateur ne peut pas accéder à la page. onChange se déclenche pour définir les informations d'identification de l'utilisateur, l'élément perd le focus. Ne devrais-je pas mettre à jour les informations d'identification à l'aide de la fonction onChange ?

import React, {Component, PropTypes} from 'react';
import {Row, Col, FormControl, FormGroup, ControlLabel, HelpBlock, Checkbox, Button} from 'react-bootstrap';

export default class Login extends React.Component {
  constructor(props, context) {
    super(props, context);

    this.state = {credentials: {username: '', password: ''}};
    this.onChange = this.onChange.bind(this);
    this.onSave = this.onSave = this.onSave.bind(this);
  }

  onChange(event) {
    const field = event.target.name;
    const credentials = this.state.credentials;
    credentials[field] = event.target.value;
    return this.setState({credentials: credentials});
    console.log(event);
  }

  onSave(event) {
    event.preventDefault();
    console.log(this.state.credentials);
    this.props.actions.logInUser(this.state.credentials);
  }

  render() {
    function FieldGroup({ id, label, help, ...props }) {
      return (
        <FormGroup controlId={id}>
          <ControlLabel>{label}</ControlLabel>
          <FormControl {...props} />
          {help && <HelpBlock>{help}</HelpBlock>}
        </FormGroup>
      );
    }

    const formInstance = (

          <Col xs={12} md={8} mdOffset={2}>
            <form>
              <FieldGroup
                name="username"
                label="Username"
                placeholder="Enter username"
                value={this.state.credentials.username}
                onChange={this.onChange}
              />
              <FieldGroup
                name="password"
                label="Password"
                type="password"
                placeholder="Enter password"
                value={this.state.credentials.password}
                onChange={this.onChange}
              />
              <Checkbox checked readOnly>
                Checkbox
              </Checkbox>

              <Button type="submit" onClick={this.onSave}>
                Submit
              </Button>
            </form>
          </Col>

    );
    return formInstance;
  }
}

Editar

Fonctionne lorsque le FieldGroup est déplacé dans un composant séparé, comme suggéré.

common/FieldGroup.jsx

import React from 'react';
import {FormControl, FormGroup, ControlLabel, HelpBlock} from 'react-bootstrap';

export default class FieldGroup extends React.Component {
  constructor(props) {
    super(props);
    console.log('props');
    console.log(props);
    this.id = props.name;
    this.label = props.label;
    this.help = props.placeholder;
  }
  render() {
    return (
      <FormGroup controlId={this.props.name}>
        <ControlLabel>{this.props.label}</ControlLabel>
        <FormControl {...this.props} />
        {this.props.help && <HelpBlock>{this.props.help}</HelpBlock>}
      </FormGroup>
    );
  }
}

composants/Login.jsx

import React, {Component, PropTypes} from 'react';
import {Col, Checkbox, Button} from 'react-bootstrap';
import FieldGroup from '../common/FieldGroup';

export default class Login extends React.Component {
  constructor(props, context) {
    super(props, context);

    this.state = {credentials: {username: '', password: ''}};
    this.onChange = this.onChange.bind(this);
    this.onSave = this.onSave = this.onSave.bind(this);
  }

  onChange(event) {
    const field = event.target.name;
    const credentials = this.state.credentials;
    credentials[field] = event.target.value;
    return this.setState({credentials: credentials});
  }

  onSave(event) {
    event.preventDefault();
    console.log(this.state.credentials);
    this.props.actions.logInUser(this.state.credentials);
  }

  render() {
    return (
      <Col xs={12} md={8} mdOffset={2}>
        <form>
          <FieldGroup
            name="username"
            label="Username"
            placeholder="Enter username"
            value={this.state.credentials.username}
            onChange={this.onChange}
          />
          <FieldGroup
            name="password"
            label="Password"
            type="password"
            placeholder="Enter password"
            value={this.state.credentials.password}
            onChange={this.onChange}
          />
          <Checkbox checked readOnly>
            Checkbox
          </Checkbox>

          <Button type="submit" onClick={this.onSave}>
            Submit
          </Button>
        </form>
      </Col>
    )
  }
}

3voto

Oblosys Points 5035

Cette question est intéressante, car elle montre que même si les composants fonctionnels sans état (SFC) n'ont pas de méthodes de cycle de vie, ils sont démontés et remontés lorsqu'ils changent. Cela n'est pas documenté très clairement, mais on peut le voir dans le fichier notes sur la mise en œuvre de react (dans le cas des SFC, type est la fonction SFC elle-même).

Parce que FieldGroup est déclaré à l'intérieur de render Une nouvelle fonction est créée à chaque rendu, ce qui a pour effet de démonter le composant SFC précédent dans le domaine virtuel et de le remplacer par un nouveau. Cela se produit même si les rendus sont exactement les mêmes. Par conséquent, tout élément d'entrée descendant sera démonté et remonté, perdant ainsi son état et son attention.

Si vous appelez simplement le SFC au lieu de l'instancier (c'est-à-dire {SFC({...sfcProps})} au lieu de <SFC {...sfcProps}/> En outre, aucun composant dom virtuel n'est créé pour le SFC lui-même, et aucun remontage ne se produira si le rendu reste le même.

Cet extrait montre un SFC local, à la fois instancié et appelé en tant que fonction, ainsi qu'un SFC normal de niveau supérieur. Seules les deux entrées inférieures fonctionnent correctement.

const SFC = ({rerender}) => <input onChange={rerender}/>

class App extends React.Component {
  rerender = () => this.forceUpdate();

  render () {
    const LocalSFC = ({rerender}) => <input onChange={rerender}/>
    return (
      <div>
        <div>Local SFC (loses state & focus on typing): <LocalSFC rerender={this.rerender}/></div>
        <div>Local function application (works fine): {LocalSFC({rerender: this.rerender})}</div>
        <div>Regular SFC (works fine): <SFC rerender={this.rerender}/></div>
      </div>
    );
  }
}
ReactDOM.render(<App/>, document.getElementById('app'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
  <div id="app"></div>
</body>

Pour résoudre le problème, vous pouvez donc remplacer chaque <FieldGroup name=.. label=.. ../> por {FieldGroup({name: .., label:.., ..})} Mais il est plus logique de faire ce que Jaxx a suggéré et de lever simplement les FieldGroup en dehors de la Login au niveau supérieur ou dans un fichier séparé.

1voto

MEnf Points 820

Le retour à l'intérieur d'un onChange lui fera perdre le focus d'un élément. Enlever return de votre onChange fonction.

Vous ne devez pas renvoyer setState .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X