SetSomeState (), вызываемый из функции catch (), не обновляется

Если есть проблема с кодом реакции, где по какой-то причине все работает должным образом, когда я обновляю пользовательский интерфейс с MyApp.promise().then(<here>), но не в моем MyApp.promise().then().catch(<here>)

  • Я знаю, что код действительно выполняется до того момента, когда я действительно вызываю setData, который является моей возвращаемой функцией useState().
  • Вызов этой функции в then() работает нормально, а не в catch().
  • исключение, которое в конечном итоге запускает catch(), отлично работает, поскольку catch() выполняется, как ожидалось
  • Я добавил console.info() в свой компонент и вижу, что он больше не перерисовывается, когда обновления приходят с catch().

Я предполагаю, что мой вопрос: что было бы особенного в функции catch(), чтобы реакция не велась бы?

Это код для моего приложения, обрабатывающего обновления статуса:

const useUpdateStatus = () => {
    const [data,setData] = useState({status: STATUS.IDLE,changelog:null,tasks:[]})

    const updateData = (d) => {
        // We call setData with an anonymous function so we can merge previous
        // data with new data
        setData((prev) => {
            console.info({ ...prev, ...d })
            return { ...prev, ...d }
        })
    };

    // Only once, we set the timer to periodically update status
    useEffect(() => {
        setInterval(() => {
            MyApp.get('/system/upgrade')
            .then((upgrade) => {
                // If anything is not "pending", it means we are upgrading
                for (var t of upgrade.tasks) {
                    if (t.status !== "pending") {
                        updateData({ status: STATUS.INSTALLING})
                    }
                }
                // updateData will call setData with the full status

                // This works as intended, UI is updated on each step
                updateData({ tasks: upgrade.tasks, changelog: upgrade.changelog})
            })
            .catch((e) => {
                // If data can't be fetched, it probably means we are restarting
                // services, so we updated the tasks array accordingly
                setData((prev) => {
                    for (var t of prev.tasks) {
                        if (t['id'] === "restarting") {
                            t['status'] = 'running'
                        }
                        else if (t['status'] == "running") {
                            t['status'] = 'finished'
                        }
                    }
                    // The expected data is logged here
                    console.info(prev)
                    return prev
                })
            })
        }, 1000);
    },[])
    return data
}

Это уровень представления:

// Using the hook :
const { changelog, tasks, status } = useUpdateStatus()

// Somewhere int he page :
<UpdateProgress tasks = {tasks}/>

// The actual components :
const UpdateProgress = (props) => {
    return(
        <div style = {{display: "flex", width: "100%"}}>
            { props.tasks.map(s => {
                return(
                    <UpdateTask key = {s.name} task = {s}/>
                )
            })}
        </div>
    )
}

const UpdateTask = (props) => {
    const colors = {
        "pending":"LightGray",
        "running":"SteelBlue",
        "finished":"Green",
        "failed":"red"
    }
    return(
        <div style = {{ textAlign: "center",  flex: "1" }}>
                <Check fill = {colors[props.task.status]} width = "50px" height = "50px"/><br/>
                <p style = {props.task.status== = "running" ? {fontWeight: 'bold'} : { fontWeight: 'normal'}}>{props.task.name}</p>
        </div>
    )
}
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
16
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

React выполняет сравнение Object.is, чтобы проверить, требуется ли повторный рендеринг после вызова обновления состояния. Поскольку вы изменяете состояние в блоке catch, response получает ложное уведомление о том, что состояние не изменилось, и, следовательно, повторный рендеринг не запускается.

Вы можете обновить свое состояние, как показано ниже, чтобы оно работало

 .catch((e) => {
            // If data can't be fetched, it probably means we are restarting
            // services, so we updated the tasks array accordingly
            setData((prev) => {
                for (var t of prev.tasks) {
                    if (t['id'] === "restarting") {
                        t['status'] = 'running'
                    }
                    else if (t['status'] == "running") {
                        t['status'] = 'finished'
                    }
                }
                // The expected data is logged here
                console.info(prev)
                return {...prev}
            })
        })

Однако лучший способ обновить состояние - сделать это неизменным способом.

  .catch((e) => {
            // If data can't be fetched, it probably means we are restarting
            // services, so we updated the tasks array accordingly
            setData((prev) => ({
                ...prev,
                tasks: prev.tasks.map((task) => {
                    if (task.id === "restarting") {
                        return { ...task, status: 'running'}
                    }
                    else if (task.id === "running") {
                        return { ...task, status: 'finished'}
                    }
                    return task
                })
            }))
        })

Спасибо, сэр, действительно работает. Если бы я только понял почему! Итак, вы сказали, что я изменяю состояние: означает ли это, что я возвращаю новый, измененный объект? т.е. ссылка на этот объект отличается, и поскольку оригинал не был изменен, реакция не перерисовывается?

Yann Bizeul 05.04.2021 13:08

Под мутацией я подразумеваю то, что вы изменяете объект состояния в той же ссылке, для обновлений состояния вы должны создать новую ссылку. Прочтите о неизменности в javascript

Shubham Khatri 05.04.2021 13:35

Ха, тогда все наоборот, обновление должно быть другим объектом, а я просто изменял возвращаемый. Вы спасли мне день, и я обязательно найду какую-нибудь литературу о неизменности!

Yann Bizeul 05.04.2021 14:06

Другие вопросы по теме