Изменение в состоянии Redux не отражается в компоненте - PullRequest
1 голос
/ 29 марта 2019

Я создаю прототип, который отображает форму входа.Событие submit запускает поиск в базе данных.Если поиск не удастся, я хочу изменить форму на а) отобразить сообщение об ошибке и б) отменить предыдущую запись для идентификатора пользователя и пароля.

Мой редуктор изменяет состояние в Redux, но я не уверенкак перевести данные обратно в состояние компонента.

Вот моя форма:

import React from 'react';
import { NavLink } from 'react-router-dom';
import { connect } from 'react-redux';

export class LoginForm extends React.Component {
    constructor(props) {
        super(props);
        console.log("Login form props", props);
        this.state = {
            userName:  props.user ? props.user.userName : '',
            password:  props.user ? props.user.password : '',
            error: props.error ? props.error : ''
        }

    }

    onUserNameChange = (event) => {
        const userName = event.target.value;
        this.setState(() => ({ userName }));
    };

    onPasswordChange = (event) => {
        const password = event.target.value;
        this.setState(() => ({ password }));
    };

    onSubmit = (event) => {
        event.preventDefault();

        if (!this.state.userName || !this.state.password) {
            this.setState(() => ({ error:  'User name and password are required.'}));
        } else {
            this.setState(() => ({ error:  '' }));
            this.props.onSubmit({
                userName:  this.state.userName,
                password:  this.state.password
            })
        }
    };

    render() {
        console.log("Login form render() this.state", this.state);
        // console.log("Login form render() this.props", this.props);
        return (
            <div>
                {this.props.error && <p>{this.props.error}</p>}
                <form onSubmit={this.onSubmit}>
                    <input
                        type="text"
                        placeholder="User name"
                        autoFocus
                        value={this.state.userName}
                        onChange={this.onUserNameChange}
                    />
                    <input
                        type="password"
                        placeholder="Password"
                        value={this.state.password}
                        onChange={this.onPasswordChange}
                    />
                    <button>Sign In</button>
                </form>
                <NavLink to="/passwordRecovery" activeClassName="is-active" exact={true}>Forgot Password?</NavLink>
                <NavLink to="/newUser" activeClassName="is-active">New User?</NavLink>
            </div>
        )
    }
}

const mapStateToProps = (state) => {
    console.log('in LoginForm state.authentication:  ', state.authentication);
    if (state.authentication.user)
    {
        return {
            error:  state.authentication.error,
            userName:  state.authentication.user.userName,
            password:  state.authentication.user.password
        }
    } else {
        return {
            error:  state.authentication.error,
            user:  state.authentication.user
        }
    }

}

export default connect(mapStateToProps, undefined)(LoginForm);

Вот страница, которая отображает форму:

import React from 'react';
import { connect } from 'react-redux';
import LoginForm from './LoginForm';
import { login, resetForm } from '../actions/authentication';

export class LoginPage extends React.Component {
    onSubmit = (user) => {
        console.log('LoginPage onSubmit user:  ', user);
        console.log('props ', this.props);
        this.props.login(user);
        if (this.props.user) {
             this.props.history.push("/userHome");
        }
    }

    render() {
        console.log("LoginPage.render()", this.props)
        return (
            <div>
                <LoginForm 
                    onSubmit={this.onSubmit} error={this.props.error}
                />
            </div>
        );
    }
}

const mapDispatchToProps = (dispatch) => ({
    login:  (user) => dispatch(login(user)),
    resetForm:  () => dispatch(resetForm())
});

const mapStateToProps = (state) => {
    console.log('state.authentication:  ', state.authentication);
    return {
        error:  state.authentication.error,
        user:  state.authentication.user
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(LoginPage);

Вот редуктор:

// reducer for authentication actions
const authenticationReducerDefaultState = {
    userName:  '',
    password:  ''
};

export default (state = authenticationReducerDefaultState, action) => {
    console.log('in reducer, state: ', state);
    console.log('in reducer, action: ', action);
    switch (action.type) {
        case 'LOGIN_REQUEST':
            return {
                user:  action.user,
                error:  '',
                loggedIn:  false,
                loggingIn:  true
            };
        case 'LOGIN_SUCCESS':
            return {
                user:  action.user,
                error:  '',
                loggedIn:  true,
                loggingIn:  false
            }
        case 'LOGIN_FAILURE':
            return {
                user:  authenticationReducerDefaultState,
                error:  action.error,
                loggedIn:  false,
                loggingIn:  false
            }
        case 'LOGOUT':
            return {
                user:  authenticationReducerDefaultState,
                error:  '',
                loggedIn:  false,
                loggingIn:  false
            };
        default:
            return state;
    };
};

Вот действие:

import database from '../firebase/firebase';

const request = (user) => ({
    type:  'LOGIN_REQUEST',
    user
});

const success = (user) => ({
    type:  'LOGIN_SUCCESS',
    user
});

const failure = (error) => {
    // console.log('failure with error ', error);
    return {
    type:  'LOGIN_FAILURE',
    user:  { userName:  '', password:  '' },
    error
}};

export const login = (user) => {
    return (dispatch) => {
        const { userName, password } = user;
        // console.log(`login function for ${userName} password ${password}`);
        dispatch(request(user));
        let matchedUser = undefined;
        return database.ref(`users`).once('value').then((snapshot) => { 
            snapshot.forEach((childSnapshot) => {
                const user = childSnapshot.val();
                if (user.userName === userName &&
                    user.password === password) {
                        matchedUser = user;
                }; 
            });
            return matchedUser;
        }).then((matchedUser) => {
            console.log('matched user', matchedUser);
            if (matchedUser) {
                dispatch(success(user));
            } else {
                // console.log('dispatching failure');
                dispatch(failure(`An error occurred looking up user ID ${userName}`));
            };
            console.log('end of login function');
        });
    }    
}

// action generator for logout action
export const logout = () => ({
    type:  'LOGOUT'
});

Вот мой корневой редуктор:

export default () => {
// Store creation
    const store = createStore(
        combineReducers({
            authentication:  authenticationReducer
        }),
        composeEnhancers(applyMiddleware(thunk))
    );
    return store;
}

Я надеюсь, что кто-то уже был на этом пути.Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 29 марта 2019

Проблема в том, что, хотя реквизиты меняются (хранилище редуксов обновляется), вы используете локальное состояние внутри LoginForm.Вы отображаете значения в реквизиты только один раз (LoginForm.constructor).Если вы хотите реагировать на изменения в хранилище с избыточностью, вам нужно написать некоторый код, чтобы обновить локальное состояние, если что-то изменится в хранилище.

static getDerivedStateFromProps (props) {
   return {
      userName:  props.user ? props.user.userName : '',
      password:  props.user ? props.user.password : '',
      error: props.error ? props.error : ''
   }
}

Все, что вы вернете в этом методе, приведет к обновлениюсостояние локального компонента.

Этот тип сценариев сложно поддерживать.Вы смешиваете понятие контролируемых и неконтролируемых компонентов.Вы получаете исходные значения из реквизитов, сопоставляете их с локальным состоянием, затем обрабатываете изменения состояния локально (при изменении входных данных), но также реагируете на изменения в хранилище.

Совет: Если вы используете реквизиты по умолчаниюВам не нужно проверять, доступен ли this.props.user.

static defaultProps = {
   user: {
      userName: '',
      password: '''
   },
   error: ''
}
0 голосов
/ 29 марта 2019

Вы пытались сбросить логику с вашей картыStateToProps

if (state.authentication.user)
{
    return {
        error:  state.authentication.error,
        userName:  state.authentication.user.userName,
        password:  state.authentication.user.password
    }
} else {
    return {
        error:  state.authentication.error,
        user:  state.authentication.user
    }
}
}

до:

return {
            error:  state.authentication.error,
            userName:  state.authentication.user.userName,
            user:  state.authentication.user
            password:  state.authentication.user.password
        }
...