Реакция JS-компонента высшего порядка не выполняет функцию, если состояние устанавливается вне этой функции - PullRequest
0 голосов
/ 13 апреля 2019

Я не могу придумать правильное предложение для заголовка вопроса, поэтому сначала я хотел бы извиниться, если название почему-то сбивает с толку.

Итак, я только что закончил изучать React JS и пытался построитьприложение для практики.В этом приложении есть форма входа в систему, которая, если введенный неверный адрес электронной почты или пароль, отобразит предупреждение и пустые поля.

С базовой реализацией React JS все хорошо, но когда я попытался изменитьмой код для реализации компонента более высокого порядка, приложение стало глючить, и я не могу найти, где виновник.

Так что глючит, , если я введу адрес электронной почты или парольнеправильно , и нажмите кнопку входа, после чего поля будут пустыми, чтобы я мог ввести / попробовать снова, но кажется, поля не будут принимать ввод , просто остается пустым нетне важно, какие клавиши я нажимал на клавиатуре, поэтому мне нужно обновить страницу, чтобы поля могли снова принять мой ввод.

Вот мой код:

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 почему-то отказывается работать для обновления полей при изменении ввода.

Кто-нибудь, пожалуйста, сообщите мне, что вызвало такое поведение?

1 Ответ

1 голос
/ 13 апреля 2019

Я просто хочу сказать, что вам не нужно использовать компонент более высокого порядка, однако, то, что вы пытаетесь сделать, может работать.

Я вижу пару проблем.

Сначала вы устанавливаете InputText value в соответствии с состоянием FormLogin. Это нормально, однако, когда InputText изменяется, он вызывает onInputChange, который установлен в setStateFormData из старшего компонента. Проблема в том, что setStateFormData устанавливает состояние для компонента высшего порядка, а не состояние для FormLogin. В результате состояние FormLogin не обновляется, что означает, что value для InputText не обновляется.

Я знаю, что вы думаете: «Но ввод обновляется, прежде чем я нажимаю« Отправить ». Да, и нет.

Это подводит нас ко второй проблеме, связанной с ссылками на JS.

Из документов 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 из старого состояния. Отлично, одна ошибка. Если мы протестируем приложение еще раз, теперь мы видим, что входные данные никогда не обновляются ни до, ни после отправки. Это из-за первой проблемы, упомянутой выше.

Раньше InputText value, казалось, обновлялся, потому что состояние FormLogin, казалось, обновлялось, однако, state.formdata в FormLogin было просто ссылкой на state.formdata в компоненте высшего порядка, который был установлен в конструкторе FormLogin с 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. Таким образом, вам не нужно беспокоиться о синхронизации двух formdata объектов. Просто создайте функцию clearFormData для компонента высшего порядка и передайте ее в FormLogin для вызова handleSubmit. Кроме того, вам нужно установить TextInput value в соответствии с реквизитами FormLogin. Я думаю, что это самое чистое решение, но при этом используется компонент высшего порядка.

Самое простое решение - это, конечно, просто избавиться от компонента более высокого порядка, но я не уверен, если это вариант, если ваша цель специально узнать о компонентах высшего порядка.

Наконец, вы можете реализовать функцию getDerivedStateFromProps для FormLogin, которая будет перестраивать состояние FormLogin каждый раз, когда изменяется его реквизит, что по совпадению происходит каждый раз, когда изменяется состояние высшего порядка. Это чистый способ обновления formdata в FormLogin каждый раз, когда он изменяется в компоненте высшего порядка. Проблема в том, что вам все еще нужно беспокоиться об обновлении formdata в компоненте высшего порядка каждый раз, когда он изменяется в FormLogin.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...