【问题标题】:useReducer value not being updated properlyuseReducer 值未正确更新
【发布时间】:2020-07-03 10:33:01
【问题描述】:

我正在尝试使用 useReducer 更新组件的状态。我在 useEffect 中从 mongodb 获取一些信息,并在那里调用我的 useReducer 调度,这样我就可以在组件安装后立即设置我的状态。不幸的是,我的状态没有受到影响,我的 UI 也没有改变。我的 useEffect 语句末尾的 console.log 语句显示我的状态仍然具有在 useReducer 调用中设置的初始值。谁能告诉我这里哪里出错了?另外,我计划稍后添加到状态和 useEffect 中的依赖数组。在我处理这个问题之前,我得到了一个无限的 useEffect 循环,所以现在我有一个空错误。不过,我想更新状态并让我的 UI 反映更改。无论如何,这里是包含 useEffect 调用的功能组件的代码。

编辑:my_player 对象最初应该是我当前与 mongodb 一起使用的用户的此对象 {health_percent: 100, hang_percent: 100, hydration_percent: 100}

const change_progress_color = (pet_percentage: any) => {
    // change the attribute's color depending on pet_attr value
    // PROGRESS_COLORS IS JUST AN ENUM OBJ WITH COLOR CODES
    let color;
    if (pet_percentage < .10)
        color = PROGRESS_COLORS[1];
    else if (pet_percentage < .30)
        color = PROGRESS_COLORS[2];
    else if (pet_percentage < .50)
        color = PROGRESS_COLORS[3];
    else if (pet_percentage < .75)
        color = PROGRESS_COLORS[4];
    else if (pet_percentage < .90)
        color = PROGRESS_COLORS[5];
    else
        color = PROGRESS_COLORS[6];
    return color;
};

const Player: React.FC = () => {
    const [player, setPlayer] = useState<any>();
    const [state, dispatch] = useReducer<React.Reducer<State, Actions>>(reducer,
        {health: 0, hydration: 0, hunger: 0});

    useEffect(() => {
        console.log("running useEffect");
        (async () => {
            var my_player = await getPlayer();
            if (my_player)
                if (my_player.length > 0) {
                    const p: any = my_player[0];
                    setPlayer(p);
                    console.log("here is my player", p);
                    dispatch({type: "ChangeHealth", diff: p.health_percent});
                    dispatch({type: "ChangeHunger", diff: p.hunger_percent});
                    dispatch({type: "ChangeHydration", diff: p.hydration_percent});
                    console.log("Here is my state", state);
                }
        })();
    }, []);
    return (
            <IonPage>
                <IonHeader>
                    <IonToolbar>
                        <IonTitle>My Player</IonTitle>
                    </IonToolbar>
                </IonHeader>
                <IonContent>
                    <IonHeader collapse="condense">
                        <IonToolbar>
                            <IonTitle size="large">My Player</IonTitle>
                        </IonToolbar>
                    </IonHeader>
                    <IonList>
                        <IonItem>
                            <IonLabel position="floating">
                                Health
                            </IonLabel>
                            <IonProgressBar
                                value={state.health}
                                color={change_progress_color(state.health)}/>
                        </IonItem>
                        <IonItem>
                            <IonLabel position="floating">
                                Hunger
                            </IonLabel>
                            <IonProgressBar
                                value={state.hunger}
                                color={change_progress_color(state.hunger)}/>
                        </IonItem>
                        <IonItem>
                            <IonLabel position="floating">
                                Hydration
                            </IonLabel>
                            <IonProgressBar
                                value={state.hydration}
                                color={change_progress_color(state.hydration)}/>
                        </IonItem>
                    </IonList>
                </IonContent>
            </IonPage>
    );
};

export default Player;

这是我在 react 函数之外的代码以及我的类型和 reducer 函数。

type State = {
    health: number;
    hunger: number;
    hydration: number;
};

type ChangeHealth = {
    readonly type: "ChangeHealth";
    readonly diff: number;  // this number is going to need to be divided by 100
}

type ChangeHunger = {
    readonly type: "ChangeHunger";
    readonly diff: number; // this number is going to need to be divided by 100
}

type ChangeHydration = {
    readonly type: "ChangeHydration";
    readonly diff: number; // this number is going to need to be divided by 100
}


type Actions = ChangeHealth | ChangeHunger | ChangeHydration;

function reducer(state: State, action: Actions): State {
    switch (action.type) {
        case "ChangeHealth":
            return (() => {
                console.log("running func");
                console.log("here is the diff ", action.diff);
                let new_health = (state.health + action.diff) / 100;
                console.log(new_health);
                if (new_health > 1)
                    new_health = 1;
                return {...state, health: new_health};
            } )();
        case "ChangeHunger":
            return (() => {
                let new_hunger = (state.hunger + action.diff) / 100;
                if (new_hunger > 1)
                    new_hunger = 1;
                return {...state, hunger: new_hunger};
            } )();
        case "ChangeHydration":
            return (() => {
                let new_hydration = (state.hydration + action.diff) / 100;
                if (new_hydration > 1)
                    new_hydration = 1;
                return {...state, hydration: new_hydration};
            } )();
    }
}

任何帮助将不胜感激。谢谢

【问题讨论】:

  • 你能用它的 html 分享整个 Player 组件吗?
  • @AleksandreJavakhishvili 刚刚做到了。谢谢你看我的问题。所以基本上我希望这些值更新,以便我可以在我的 IonProgress 栏中使用它们。根据这些状态值,我也会更改进度条的颜色
  • 我还有一个问题,console.log("here is my player", p); 这一行记录了什么?
  • @AleksandreJavakhishvili 我用我的玩家 _id、我的用户 id 和以下键健康百分比、饥饿百分比、水合百分比都为 100 取回了一个对象。所以我将值传递给已定义且大于的调度0
  • 最后一个console.log在useEffect中打印错误状态的原因是因为dispatch(它不是同步的)方法导致重新渲染,新的状态会在新的渲染中更新

标签: reactjs typescript


【解决方案1】:

您的 useEffect 和 Reducer 代码很好,状态如您预期的那样已更新,最后一个 console.log 没有记录新状态的原因是因为调度方法不同步,它导致重新渲染并在下一个渲染新状态将被更新。我认为您的问题是显示状态 &lt;IonProgressBar value={state.hydration} color={change_progress_color(state.hydration)}/&gt;.

【讨论】:

  • 刚刚发现 ion 不喜欢我提供给进度条的颜色。不过,其他一切都正常。感谢您的帮助。
【解决方案2】:

您只提出一个请求。您出于什么原因将数据拆分为 useState 和 useReduce?

下面的 MB 代码可以帮助您。

import React, { useEffect, useState } from 'react';

const Player: React.FC = () => {
  const [player, setPlayer] = useState({
    health: 0,
    hydration: 0,
    hunger: 0,
  });
  
  useEffect(() => {
    getPlayer()
      .then((response) => {
        const [player] = response;

        if (player) {
          setPlayer((prevState) => {
            const health = (prevState.health + player.health_percent) / 100;
            const hunger = (prevState.hunger + player.hunger_percent) / 100;
            const hydration = (prevState.hydration + player.hydration_percent) / 100;

            return ({
              health: health > 1 ? 1 : health,
              hunger: hunger > 1 ? 1 : hunger,
              hydration: hydration > 1 ? 1 : hydration,
            });
          });
        }
      })
      .catch(({ message }) => {
        console.error(message);
      });
  }, []);

  return null;
};

【讨论】:

  • 我想试试 useReducer(1st time user) 并想看看当我将第二个 arg 数组中的依赖项设置为 useEffect() 时是否可以解决我的无限 useEffect 循环问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-10
  • 2018-02-24
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 1970-01-01
  • 2021-10-26
相关资源
最近更新 更多