从函数组件中的 Redux Store 接收新值时,useState 不起作用

发布于 2025-01-10 19:16:26 字数 3396 浏览 1 评论 0原文

在此 StackBlitz (你可以玩) 我有以下代码:

App.js

import { Field, Form, Formik } from 'formik';
import React, { useState } from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';
import store, { userActions } from './store';

const Suggestion = () => {

  const dispatch = useDispatch();
  const { color } = useSelector(state => state.user);

  return (color === 'Yellow') ? (
    <div>
      We recommend you to change to Green.<br />
      <a href="#" onClick={() => { dispatch(userActions.save({ color: 'Green' })); }}>Change Now!</a>
    </div>
  ) : null;
};

const MyComp = () => {

  const dispatch = useDispatch();
  const userData = useSelector(state => state.user);

  const initialValues = {
    color: 'Green',
  };

  const [ formValues, setFormValues ] = useState((userData.length > 0) ? userData : initialValues);

  console.log(JSON.stringify({ userData, formValues }));

  const onSubmit = (values) => {
    dispatch(userActions.save(values));
  };

  return (
    <Formik
      initialValues={formValues}
      onSubmit={onSubmit}
      enableReinitialize
    >
      {formik => {

        const handleChange = (partialUserData) => {
          setFormValues({
            ...formValues,
            ...partialUserData
          });
          formik.submitForm();
        };

        return (
          <Form>
            <h2>What Color do you use?</h2>
            <Field id="color" as="select" value={formValues.color} onChange={(e) => { handleChange({ color: e.target.value }); }}>
              <option value=''>Select...</option>
              <option value='Green'>Green</option>
              <option value='Yellow'>Yellow</option>
            </Field>
            <Suggestion />
          </Form>
        );
      }}
    </Formik>
  );
};

export default () => {
  return (
    <Provider store={store}>
      <MyComp />
    </Provider>
  );
};

store.js

import { configureStore, createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {},
  reducers: {
    save(state, action) {
      for (const key in action.payload) {
        state[key] = action.payload[key];
      }
    },
  },
});

const store = configureStore({
  reducer: {
    user: userSlice.reducer,
  },
  devTools: true,
});

export const userActions = userSlice.actions;

export default store;

预期功能

  1. 默认选择“绿色”
  2. 您选择“黄色”,下面会出现一条建议 (你得到:控制台上的“黄色”)
  3. 您选择“绿色”,建议消失(您在控制台上看到:“绿色”)
  4. 执行上面的步骤 2
  5. 您单击链接:“立即更改!”
  6. 选项“绿色”被选中并且建议消失(您在控制台上看到:“绿色”)

我的问题是:

第 6 步没有完全起作用,因为即使建议消失(这很好),该选项:“绿色”没有被选择,在控制台上我仍然得到:“黄色”。

由于某种原因,该行...

const [ formValues, setFormValues ] = useState(...);

...没有保存变量中存储的值:formValues,因为userData具有预期值,如您在下面的图片...

在此处输入图像描述

任意关于如何使这项工作有效的想法?

谢谢!

On this StackBlitz (you can play with) I have the following code:

App.js

import { Field, Form, Formik } from 'formik';
import React, { useState } from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';
import store, { userActions } from './store';

const Suggestion = () => {

  const dispatch = useDispatch();
  const { color } = useSelector(state => state.user);

  return (color === 'Yellow') ? (
    <div>
      We recommend you to change to Green.<br />
      <a href="#" onClick={() => { dispatch(userActions.save({ color: 'Green' })); }}>Change Now!</a>
    </div>
  ) : null;
};

const MyComp = () => {

  const dispatch = useDispatch();
  const userData = useSelector(state => state.user);

  const initialValues = {
    color: 'Green',
  };

  const [ formValues, setFormValues ] = useState((userData.length > 0) ? userData : initialValues);

  console.log(JSON.stringify({ userData, formValues }));

  const onSubmit = (values) => {
    dispatch(userActions.save(values));
  };

  return (
    <Formik
      initialValues={formValues}
      onSubmit={onSubmit}
      enableReinitialize
    >
      {formik => {

        const handleChange = (partialUserData) => {
          setFormValues({
            ...formValues,
            ...partialUserData
          });
          formik.submitForm();
        };

        return (
          <Form>
            <h2>What Color do you use?</h2>
            <Field id="color" as="select" value={formValues.color} onChange={(e) => { handleChange({ color: e.target.value }); }}>
              <option value=''>Select...</option>
              <option value='Green'>Green</option>
              <option value='Yellow'>Yellow</option>
            </Field>
            <Suggestion />
          </Form>
        );
      }}
    </Formik>
  );
};

export default () => {
  return (
    <Provider store={store}>
      <MyComp />
    </Provider>
  );
};

store.js

import { configureStore, createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {},
  reducers: {
    save(state, action) {
      for (const key in action.payload) {
        state[key] = action.payload[key];
      }
    },
  },
});

const store = configureStore({
  reducer: {
    user: userSlice.reducer,
  },
  devTools: true,
});

export const userActions = userSlice.actions;

export default store;

Expected functionality

  1. "Green" is selected by default
  2. You select "Yellow" and a suggestion appears underneath (you get: "Yellow" on the console)
  3. You select "Green" and the suggestion disappears (you get: "Green" on the console)
  4. Do Step 2 above
  5. You click the link: "Change Now!"
  6. Option "Green" gets selected and the suggestion disappears (you get: "Green" on the console)

My Problem is:

Step 6 is not totally working because even though the suggestion disappears which is fine, the option: "Green" doesn't get selected and on the console I still get: "Yellow".

For some reason, the line...

const [ formValues, setFormValues ] = useState(...);

... is not saving values from the Store on variable: formValues since the userData has the expected values as you can see on the image below...

enter image description here

Any idea on how to make this work?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

平安喜乐 2025-01-17 19:16:26

不,这是错误的 React.useState(defaultValue) 它只是用于组件渲染的 React.useState 的默认值,它不会监听新的更改。

您需要使用 React.useEffect() 钩子,如果您将依赖项数组留空,则它会获取依赖项数组,然后 useEffect 将仅在组件渲染时调用一次,但如果您通过依赖作为您的 redux 数据,然后每次您的依赖数据发生更改时,都会调用 useEffect

所以最后你的代码将是这样的

const dispatch = useDispatch();
  const userData = useSelector(state => state.user);

  const initialValues = {
    color: 'Green',
  };

  const [ formValues, setFormValues ] = useState(initialValues);
  
  React.useEffect(()=>{
    setFormValues((userData.length > 0) ? userData : initialValues);
    // cleanup function which will be invoked on component unmount
    return ()=> {
      setFormValues(userData)
    }
  },[useData]);

  console.log(JSON.stringify({ userData, formValues }));

  const onSubmit = (values) => {
    dispatch(userActions.save(values));
  };

no that is wrong React.useState(defaultValue) it's just the default value for the React.useState which is used on component render and it does not listen for new changes.

You need to use React.useEffect() hook which takes the dependencies array if you leave the dependencies array empty then useEffect will only be invoked once on component render but if you pass dependency as your redux data then every time your dependency data is changed useEffect will be invoked.

So finally you code will be like this

const dispatch = useDispatch();
  const userData = useSelector(state => state.user);

  const initialValues = {
    color: 'Green',
  };

  const [ formValues, setFormValues ] = useState(initialValues);
  
  React.useEffect(()=>{
    setFormValues((userData.length > 0) ? userData : initialValues);
    // cleanup function which will be invoked on component unmount
    return ()=> {
      setFormValues(userData)
    }
  },[useData]);

  console.log(JSON.stringify({ userData, formValues }));

  const onSubmit = (values) => {
    dispatch(userActions.save(values));
  };
毁梦 2025-01-17 19:16:26

我不知道捕获新存储值的正确方法是什么。但我可以建议的是创建一个 useEffect 挂钩来设置每次 userData 更改时运行的新表单值。

useEffect(() => {
    setFormValues(userData)
  }, [userData])

I don't know what is the correct way to capture the new stored value. But what i can suggest is to create a useEffect hooks to set new form values that runs every time userData changes.

useEffect(() => {
    setFormValues(userData)
  }, [userData])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文