React Hook setState передается во время работы пропеллера, но фактически не меняет состояние - PullRequest
0 голосов
/ 03 февраля 2020

Я пытаюсь настроить функцию входа для приложения, которое я создаю, и у меня возникают проблемы с обновлением объекта «пользователь» после успешного входа в систему. Что странно с 'console.log ()' Я знаю, что функция запускается, и даже показывает правильную информацию, переданную в функцию, но затем состояние не обновляется. Я знаю, что он не обновляется, потому что я сделал кнопку, чтобы изменить свой статус «isAuthenticated» на «true», и это прекрасно работает, и тот же «console.log ()» показывает ту же самую информацию. Я уверен, что это может работать как класс, но я действительно пытаюсь сделать это приложение только крюками, чтобы получить больше практики. Я уверен, что нарушаю одно из правил хуков, но не знаю, как это исправить.

// App.js

function App() {

    const [auth, setAuth] = React.useState({
        isAuthenticated: false,
        user: null,  // Expects Cognito User Token
    });

    const setAuthStatus = authStatus => {
        setAuth({...auth, isAuthenticated: authStatus});
        console.log(authStatus, auth)
    };

    const setUser = user => {
        setAuth({...auth, user: user});
    };

    const authProps = {  // Wrapping for easy pass down
        isAuthenticated: auth.isAuthenticated,
        user: auth.user,
        setAuthStatus: setAuthStatus,
        setUser: setUser,
    };

return (
        <BrowserRouter>
            <NavBar auth={authProps}/>
            <button onClick={() => authProps.setAuthStatus(!auth.isAuthenticated)}>Click</button>
            <Switch>
                <Route exact path={"/"} component={LandingPage}/>
                <Route exact path={"/main"} render={(props) => <MainWindow {...props} auth={authProps}/>}/>
                <Route exact path={"/register"} render={(props) => <Register {...props} auth={authProps}/>}/>
                <Route exact path={"/register/success"} component={RegisterSuccess}/>
                <Route exact path={"/login"} render={(props) => <Login {...props} auth={authProps}/>}/>
                <Route exact path={"/logout"} render={(props) => <Logout {...props} auth={authProps}/>}/>
                <Route component={NotFound}/>
            </Switch>
            <Footer/>
        </BrowserRouter>
    );
}

export default App;

Я обернул функции и переменные состояния и передал их. Эта кнопка - кнопка тестирования, которая работает нормально.

Login.jsx

function Login(props) {
    // styles stuff, and form validation thingies

    const handleSubmit = async event => {
        event.preventDefault();

        clearErrorState();
        try {
            const user = await Auth.signIn(values.username, values.password);
            props.auth.setAuthStatus(true);  // *** Fires, but no state change ***
            props.auth.setUser(user);
            props.history.push("/main")
        } catch (error) {  // Sometimes returned as error, or error.message
            let err = null;
            !error.message ? err = {"message": error} : err = error;  // Normalize
            setValues({
                ...values, errors: {cognito: err}
            });
        }
    };

Я даже сделал образец кнопки в форме Login.jsx, и она смогла изменить состояние с точно таким же 'props.auth.setAuthStatus 'функция, использующая тот же стиль лямбда, что и в файле приложения. js.

Мне не удалось найти ни примеров, ни кода, который, по-видимому, сталкивался с той же проблемой. Есть примеры setState внутри блоков try / catch, но ни в одном из них вы не передаете его от родителя.

  • В качестве отступления, я решил не использовать Context или Redux для этого приложения в данный момент, но если Мне нужен один из них, чтобы сделать эту работу, пожалуйста, дайте мне знать, и я с удовольствием включу его. Я не использовал их, но только потому, что приложение не будет таким большим.

1 Ответ

0 голосов
/ 03 февраля 2020

Хорошо, разобрался, и я надеюсь, что это поможет другим. Это специфическая c проблема с ловушкой, и мне грустно, что мне потребовалось целое время на ее решение.

Если бы у меня был Redux Dev Tools, я бы решил это за пять минут.

I наконец сделал фиктивную кнопку, которая показала мне состояние после входа в систему и обнаружила мою проблему. Поскольку у меня в состоянии «auth» было «isAuth» и «user», я делал два отдельных вызова, чтобы изменить состояние при входе в систему. В React на основе классов каждый вызов setState просто перезаписывает значение, которое вы меняете, однако поскольку Hooks требует, чтобы вы скопировали исходное состояние, а затем также изменили свое значение, я отправлял два изменения состояния одновременно.

Из-за забавного асинхронного изменения состояния второй вызов setState захватил бы исходное состояние укажите состояние и поместите пользователя Cognito, сохраняя при этом значение isAuth равное false в исходном состоянии, а затем перезаписав первый вызов setState, который изменил его на true.

Я исправил это, поставив каждое состояние находится в своем собственном состоянии: [auth, setAuth] и [user, setUserToken], поэтому у каждого есть свой отдельный вызов.

TLDR: при отправке нескольких вызовов setState в один и тот же объект состояния с использованием хуков, каждый Вызов setState может в итоге перезаписать ваши другие вызовы старой информацией. Разделите ваши состояния, если вам нужно обновить их в одной и той же функции.

...