web-dev-qa-db-fra.com

React - change l'entrée defaultValue en passant des accessoires

Considérez cet exemple:

var Field = React.createClass({
    render: function () {
        // never renders new value...
        return (
            <div>
                <input type="text" defaultValue={this.props.value || ''} />
            </div>
        );
    }
});

var App = React.createClass({
    getInitialState: function () {
        return {value: 'Hello!'};
    },

    changeTo: function (str) {
        this.setState({value: str});
    },

    render: function () {
        return (
            <div>
                <Field value={this.state.value} />
                <button onClick={this.changeTo.bind(null, 'Whyyyy?')}>Change to "Whyyyy?"</button>
                <button onClick={this.changeTo.bind(null, void 0)}>Change to undefined</button>
            </div>
        );
    }
});

React.render(
    <App />,
    document.getElementById('app')
);

Je veux passer la valeur dans defaultValue en tant que prop du composant d'entrée stupide. Cependant, il ne le restitue jamais.

21
Kosmetika

Comme mentionné dans une réponse précédente, defaultValue n'est défini qu'à la charge initiale d'un formulaire. Après cela, il ne sera pas mis à jour "naturellement" car l'intention était uniquement de définir une valeur par défaut initiale.

Vous pouvez contourner ce problème si vous en avez besoin en passant un key au composant wrapper, comme sur votre composant Field ou App, bien que dans des circonstances plus pratiques, ce serait probablement être un composant form. Un bon key serait une valeur unique pour la ressource transmise au formulaire - comme l'id stocké dans la base de données, par exemple.

Dans votre cas simplifié, vous pouvez le faire dans votre rendu de champ:

<div key={this.props.value}>
    <input type="text" defaultValue={this.props.value || ''} />
</div>

Dans un cas de formulaire plus complexe, quelque chose comme ceci pourrait obtenir ce que vous voulez si, par exemple, votre action onSubmit soumise à une API mais restée sur la même page:

const Form = ({item, onSubmit}) => {
  return (
    <form onSubmit={onSubmit} key={item.id}>
      <label>
        First Name
        <input type="text" name="firstName" defaultValue={item.firstName} />
      </label>
      <label>
        Last Name
        <input type="text" name="lastName" defaultValue={item.lastName} />
      </label>
      <button>Submit!</button>
    </form>
  )
}

Form.defaultProps = {
  item: {}
}

Form.propTypes = {
  item: PropTypes.object,
  onSubmit: PropTypes.func.isRequired
}

Lorsque vous utilisez des entrées de formulaire non contrôlées, nous ne nous soucions généralement pas des valeurs avant qu'elles ne soient soumises, c'est pourquoi il est plus idéal de ne forcer un nouveau rendu que lorsque vous voulez vraiment mettre à jour les valeurs par défaut (après la soumission, pas à chaque changement de l'entrée individuelle).

Si vous modifiez également le même formulaire et craignez que la réponse de l'API ne revienne avec des valeurs différentes, vous pouvez fournir une clé combinée de quelque chose comme id plus l'horodatage.

50
Sia

defaultValue ne fonctionne que pour la charge initiale. Après cela, il ne sera pas mis à jour. Vous devez maintenir l'état pour vous Field component:

var Field = React.createClass({
    //transfer props to state on load
    getInitialState: function () {
        return {value: this.props.value};
    },
    //if the parent component updates the prop, force re-render
    componentWillReceiveProps: function(nextProps) {
         this.setState({value: nextProps.value});
    },
    //re-render when input changes
    _handleChange: function (e){
        this.setState({value: e.target.value});
    },
    render: function () {
        // render based on state
        return (
            <div>
                <input type="text" onChange={this._handleChange} 
                                   value={this.state.value || ''} />
            </div>
        );
    }
});
12
drogon

Je suis assez certain que cela a à voir avec entrées contrôlées vs entrées non contrôlées .

Si je comprends bien, puisque votre <input> n'est pas contrôlé (ne définit pas d'attribut value), la valeur sera toujours résolue à la valeur avec laquelle elle est initialisée. Dans ce cas Hello!.

Afin de surmonter ce problème, vous pouvez ajouter un attribut value et le définir pendant le onChange:

var Field = React.createClass({
      render: function () {
          // never renders new value...
          return (
              <div>
                  <input type="text" defaultValue={this.props.default || ''} value={this.props.value} />
              </div>
          );
      }
  });

Voici un plongeur montrant le changement.

4
Davin Tryon

Je suis également confronté à ce problème, ce que j'ai fait était de mettre à jour manuellement la valeur d'entrée lorsque les accessoires ont changé. Ajoutez ceci à votre classe Field react:

componentWillReceiveProps(nextProps){
    if(nextProps.value != this.props.value) {
        document.getElementById(<element_id>).value = nextProps.value
    }
}

Vous avez juste besoin d'ajouter un attribut id à votre élément pour qu'il puisse être localisé.

0
Kael Vergara

Utilisez le rendu conditionnel, le composant chargera la valeur initiale correcte. Quelque chose comme dans ce module:

class MenuHeaderInput extends React.Component{
    constructor(props){
        super(props);
        this.handleBlur = this.handleBlur.bind (this);
    }
    handleBlur (e) {
        this.props.menuHeaderUpdate(e.target.value);
    }
    render(){
        if (this.props.menuHeader) {
            return (
                <div className="w3-row w3-margin" onClick = {() => this.props.handleTitleClick (10)}>
                    <div className="w3-third" ><pre></pre></div>
                    <input
                        className = {"w3-third w3-input w3-jumbo " + EDIT_COLOR}                
                        type = "text"
                        defaultValue = {this.props.menuHeader}
                        onBlur = {this.handleBlur}
                    />
                    <div className="w3-third" ><pre></pre></div>                
                </div>
            )
        }
        else {
            return null;
        }
    }
}
0
Kaj Risberg

Le problème est ici:

onClick={this.changeTo.bind(null, 'Whyyyy?')}

Je suis curieux de savoir pourquoi vous vous liez à null.

Vous voulez vous lier à "ceci", de sorte que changeTo définiraState dans CET objet.

Essaye ça

<button onClick={this.changeTo.bind(this, 'Whyyyy?')}>Change to "Whyyyy?"</button>
<button onClick={this.changeTo.bind(this, void 0)}>Change to undefined</button>

En Javascript, quand une fonction est appelée, elle est appelée dans la portée d'où elle a été appelée, pas d'où elle a été écrite (je sais, cela semble contre-intuitif). Pour vous assurer qu'il est appelé dans le contexte où vous l'écrivez, vous devez '.bind (this)'.

Pour en savoir plus sur la liaison et la portée des fonctions, il existe de nombreux tutes en ligne (certains bien meilleurs que d'autres) - vous aimerez peut-être celui-ci: http://ryanmorr.com/understanding-scope-and-context- in-javascript /

Je recommande également d'utiliser les outils React Dev si vous utilisez Firefox ou Chrome, de cette façon vous auriez pu voir que state.message ne changeait pas: https: // facebook.github.io/react/blog/2015/09/02/new-react-developer-tools.html

0
Frazer Kirkman

Vous pouvez effectuer l'entrée de manière conditionnelle, puis chaque fois que vous souhaitez forcer une mise à jour de defaultValue, il vous suffit de démonter l'entrée, puis de la restituer immédiatement.

0
Bnaya Zil