質問のタイトルの適切な文が思いつかないので、最初にタイトルが混乱している場合はお詫び申し上げます。
だから私はReactJSを学び、練習用のアプリを作ろうとしていました。このアプリには、入力したメールアドレスまたはパスワードが間違っている場合にアラートが表示され、フィールドが空白になるログインフォームがあります。
基本的なReactJSの実装はすべて順調ですが、コードを変更して高次のコンポーネントを実装しようとすると、アプリがバグになり、原因がどこにあるのかわからないようです。
バギー事があるので、私は電子メールまたは誤ったパスワードを入力した場合、およびフィールドが入力に私のためにブランクされるよりも、ログインをクリック/もう一度試して、思わフィールドはすべての入力を受け付け文句を言わない、それだけで、空白のまましないに関係なく、キーボードでどのキーを押したか。フィールドが入力を再び受け入れることができるようにページを更新する必要があります。
これが私のコードです:
function ResponseAlert(props){
if(props.alertStatus!==undefined && props.alertStatus!==null){
const className = 'alert alert-' + props.alertStatus;
return <div className={className}>{props.alertMessage}</div>
}
return <div></div>;
}
function H2Title(props) {
return <h2>{props.title}</h2>;
}
class InputText extends React.Component{
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(evt){
this.props.onInputChange(evt.target.id, evt.target.value);
}
render(){
return (
<div className="form-group">
<label htmlFor={this.props.elemId}>{this.props.label}</label>
<input type={this.props.type} className="form-control" name={this.props.elemId} id={this.props.elemId} value={this.props.elemValue} onChange={this.handleChange} required={this.props.required} />
</div>
);
}
}
function withSetStateFormData(WrappedComponents, componentName){
return class extends React.Component{
constructor(props){
super(props);
this.state = { formdata: { email: '', password: '' } };
this.setStateFormData = this.setStateFormData.bind(this);
}
setStateFormData(field, value){
this.setState(function(state, props){
let tempData = state.formdata;
tempData[field] = value;
return {
formdata: tempData
};
});
}
render(){
return <WrappedComponents handleInputChange={this.setStateFormData} {...this.props} {...this.state} />
}
}
}
class FormLogin extends React.Component {
constructor(props){
super(props);
this.state = {
alertstatus: null,
alertmessage: '',
formdata: this.props.formdata
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(evt){
evt.preventDefault();
const self = this;
fetch("http://someurl.com/login.php",
{
mode: 'cors',
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(this.state.formdata)
})
.then(function (response) {
if (response.status === 200) {
response.json().then(function (resData) {
alert('Yeay! Successful login.');
});
}
else{
//here is where the problem lies, I think.
self.setState({
alertstatus: 'danger',
alertmessage: 'Login failed. Email or password is incorrect.',
formdata: { email: '', password: '' }
});
}
})
.catch(function (err) {
console.log(err);
});
return false;
}
render(){
const handleInputChange = this.props.handleInputChange;
return (
<div>
<ResponseAlert alertStatus={this.state.alertstatus} alertMessage={this.state.alertmessage} />
<H2Title title="Login"/>
<form onSubmit={this.handleSubmit}>
<InputText type="email" elemId="email" label="Email Address" elemValue={this.state.formdata.email} onInputChange={handleInputChange} required={true}/>
<InputText type="password" elemId="password" label="Password" elemValue={this.state.formdata.password} onInputChange={handleInputChange} required={true}/>
<button type='submit' className='btn btn-primary'>Login</button>
</form>
</div>
);
}
}
const EnhancedComponent = withSetStateFormData(FormLogin, "login");
ReactDOM.render(<EnhancedComponent />, document.querySelector('#app'));
したがって、setState関数を実行して電子メールとパスワードを空白にした後、onChangeイベントは、入力変更のフィールドを更新するための作業をどういうわけか拒否すると思います。
誰かが私にこの行動を引き起こしているものを教えてください?
ここでは高次のコンポーネントを使用する必要はありませんが、実行しようとしていることは機能します。
私が見るいくつかの問題があります。
まず、状態にInputText
value
応じて設定しますFormLogin
。それは問題ありませんが、InputText
変更onInputChange
されるsetStateFormData
と、上位コンポーネントからに設定されているものが呼び出されます。問題はsetStateFormData
、の状態ではなく、高次のコンポーネントの状態を設定することですFormLogin
。その結果、FormLogin
状態は更新さvalue
れInputText
ません。つまり、forは更新されません。
「送信をクリックする前に入力が更新されます」とあなたが考えていることはわかっています。はいといいえ。
これにより、JS参照に関係する2番目の問題が発生します。
Reactドキュメントから
state
変更が適用されているときのコンポーネントの状態への参照です。直接変更しないでください。代わりに、state
とからの入力に基づいて新しいオブジェクトを作成することにより、変更を表す必要がありprops
ます。
私たちが見ると、最初にでオブジェクトを参照し、次に参照setStateFormData
さstate
れたオブジェクトをstate
でlet tempData = state.formdata;
変更することによって、技術的に直接変更していますtempData[field] = value;
。これは、このようなあらゆる種類の紛らわしいバグにつながるため、Reactでは大したことではありません。
古い状態から新しい状態を構築できますが、コピーは値のみが許可され、参照は許可されません。これは、setStateFormData
次のように書き直すことで修正できます。
setStateFormData(field, value){
this.setState(function(state, props){
let newFormData = {
email: state.formdata.email;
password: state.formdate.password;
};
newFormData[field] = value;
return {
formdata: newFormData
};
});
}
現在、formdata
古い状態からを深くコピーしています。素晴らしい、1つのバグがあります。アプリケーションを再度テストすると、送信の前後に入力が更新されないことがわかります。これは、上記の最初の問題が原因です。
以前は、状態InputText
value
が更新されてFormLogin
いるように見えたため、更新されているように見えましたが、state.formdata
inFormLogin
はstate.formdata
、FormLogin
withのコンストラクターで設定された高階コンポーネント内の参照にすぎませんでしたformdata: this.props.formdata
。これは、setStateFormData
高次コンポーネントの状態を直接変更すると、の状態も直接変更されることを意味しましたFormLogin
。これにより、すべてが機能しているように見えましたが、実際には参照のためでした。参照が失われるとすぐに、アプリが壊れました。では、いつ参照を失ったのでしょうか。ご想像のとおり、を呼び出すsetState
とhandleSubmit
。
self.setState({
alertstatus: 'danger',
alertmessage: 'Login failed. Email or password is incorrect.',
formdata: { email: '', password: '' }
});
これは新しいオブジェクトに割り当てられstate.formdata
、高次コンポーネントのFormLogin
への参照を壊しstate.formdata
ます。
さて、それを修正する方法。いくつかの方法があります。
完全にformdata
状態から外すことをお勧めFormLogin
します。そうすれば、2つのformdata
オブジェクトの同期を維持することを心配する必要はありません。clearFormData
高階コンポーネントで関数を作成し、それをに渡してFormLogin
を呼び出すだけですhandleSubmit
。さらに、TextInput
value
の小道具に従って設定する必要がありますFormLogin
。これは、高次のコンポーネントを使用しながら、最もクリーンなソリューションだと思います。
最も簡単な解決策は、もちろん高次のコンポーネントを取り除くことですが、あなたの目標が特に高次のコンポーネントについて学ぶことである場合、それがオプションであるかどうかはわかりません。
最後に、getDerivedStateFromProps関数を実装できます。この関数FormLogin
ではFormLogin
、小道具が変更されるたびに状態が再構築されます。これは、高次の状態が変更されるたびに偶然に発生します。これは、更新するためのクリーンな方法ですformdata
でFormLogin
、それは高次の成分に変化し、すべての時間を。問題は、formdata
で変更されるたびに、上位コンポーネントのを更新することを心配する必要があることですFormLogin
。
これがお役に立てば幸いです。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加