Состояние React Redux не отображается с помощью библиотеки React-Router

Я создаю форму опроса с повторно используемыми компонентами React для макета страницы с состоянием, поступающим из моего магазина Redux. Мое состояние обновилось, но обновленное состояние не отображается на странице должным образом. Конкретно на маршруте обзора мои item.value отсутствуют. Как я могу получить значения для рендеринга?

Приложение.jsx

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { Route, useHistory } from 'react-router-dom';

function App() {
  const history = useHistory();
  const dispatch = useDispatch();
  const feedbackSchema = useSelector((store) => store.feedbackSchema);
  const [inputValue, setInputValue] = useState('');

  useEffect(() => {
    fetchFeedback();
  }, []);

  const fetchFeedback = () => {
    axios({
      method: 'GET',
      url: 'api/feedback',
    })
      .then((response) => {
        dispatch({
          type: 'SET_FEEDBACK',
          payload: response.data,
        });
      })
      .catch((error) => {
        console.info('You had a axios GET error', error);
      });
  };

  const handleSubmit = (item, index) => {
    console.info(item, inputValue);
    dispatch({
      type: 'SET_VALUE',
      payload: [item, inputValue],
    });
    history.push(
      feedbackSchema[index + 1]?.route
        ? `/${feedbackSchema[index + 1]?.route}`
        : '/review'
    );
  };

  return (
    <div className = "App">
      <header className = "App-header">
        <h1 className = "App-title">Feedback!</h1>
        <h4>Don't forget it!</h4>
      </header>
      {feedbackSchema.map((item, index) => {
        return (
          <div key = {item.key} className = "feedback-container">
            <Route exact path = {`/${item.route}`}>
              <h1>{item.header}</h1>
              <form>
                <div className = "feedback-input">
                  <p className = "feedback-topic">{item.topic}</p>
                  <input
                    type = "text"
                    value = {inputValue}
                    onChange = {(event) => setInputValue(event.target.value)}
                  />
                  <br />
                  <button
                    type = "button"
                    onClick = {() => handleSubmit(item.key, index)}
                  >
                    Next
                  </button>
                </div>
              </form>
            </Route>
          </div>
        );
      })}
      <Route exact path = {`/review`}>
        <h1>Review Your Feedback</h1>
        {feedbackSchema.map((feedback) => {
          return (
            <p key = {feedback.key}>
              {JSON.stringify(feedback)}
              {feedback.key}: {feedback.value}
            </p>
          );
        })}
      </Route>
    </div>
  );
}

export default App;

Магазин.js

import { applyMiddleware, combineReducers, createStore } from "redux";
import logger from "redux-logger";

const feedbackList = (state = [], action) => {
  if (action.type === 'SET_FEEDBACK') {
    return action.payload;
  }
  return state;
}

const feedbackSchema = (
  state = [
    { key: "feeling", route: "", header: "How are you feeling today?", topic: "Feeling?", value: "" },
    { key: "understanding", route: "understanding", header: "How well are you understanding the content?", topic: "Understanding?", value: "" },
    { key: "support", route: "support", header: "How well are you being supported?", topic: "Support?", value: "" },
    { key: "comments", route: "comments", header: "Any comments you want to leave?", topic: "Comments", value: "" }
  ],
  action
) => {
  if (action.type === 'SET_VALUE') {
    state.map((feedback) => {
      if (feedback.key === action.payload[0]) {
        feedback.value = action.payload[1]
      }
      return feedback;
    })
  }
  return state;
}

const store = createStore(
  combineReducers({
    feedbackList,
    feedbackSchema,
  }),
  applyMiddleware(logger)
)

export default store;

index.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './components/App/App';
import { Provider } from 'react-redux';
import { HashRouter as Router } from 'react-router-dom';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store = {store}>
      <Router>
        <App />
      </Router>
    </Provider>
  </React.StrictMode>
);

Можете ли вы попробовать добавить новый файл проверки и использовать обратную связь от useSelector, чтобы проверить, получил ли он обновленное значение. Я думаю, что в настоящее время при изменении маршрута состояние из избыточного кода не извлекается.

Sakshi Mahajan 04.05.2024 10:02

@SakshiMahajan - Я думал, что проблема в этом, но на самом деле я получал данные обратно из редуктора, и на самом деле я не сохранял и не сохранял обновленное состояние в переменной.

Suz 04.05.2024 16:29

Вы изменяете состояние, но не возвращаете ссылку на новое состояние, используйте return state.map((feedback) => .....) в условии.

Drew Reese 05.05.2024 01:47
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
3
69
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Состояние обновляется, но пользовательский интерфейс не обновляется, поскольку react-redux проверяет, изменились ли данные, сравнивая адрес данных.

В вашем Store.js файле

const feedbackSchema = (state = [], action) => {
    if (action.type === 'SET_VALUE') {
        state.map((feedback) => {
            if (feedback.key === action.payload[0]) {
                feedback.value = action.payload[1]
            }
            return feedback;
        })
    }
    return state;
}

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

Вместо этого вы можете сделать это,

const feedbackSchema = (state = [], action) => {
    if (action.type === 'SET_VALUE') {
       const temp = state.map((feedback) => {
            if (feedback.key === action.payload[0]) {
                feedback.value = action.payload[1]
            }
            return feedback;
        })
    }
    return temp;
}

Вернув новую переменную temp, редуктор поймет, что изменения внесены, и пользовательский интерфейс будет обновлен.

К вашему сведению, это терпит неудачу, когда action.type имеет любое значение, кроме "SET_VALUE", и это все равно позволяет произойти мутации состояния.

Drew Reese 05.05.2024 02:04

Проблема

Вы изменяете состояние и не возвращаете новые ссылки на состояние.

const feedbackSchema = (state = [....], action) => {
  if (action.type === 'SET_VALUE') {
    state.map((feedback) => {
      if (feedback.key === action.payload[0]) {
        feedback.value = action.payload[1] // <-- mutation!!!
      }
      return feedback;
    })
  }
  return state; // <-- same reference as previous state
}

React работает, используя поверхностные проверки на равенство ссылок, и если state — это та же ссылка, что и в предыдущем цикле рендеринга, React не обязательно запускает повторный рендеринг компонента.

Предложения по решению

Чтобы правильно решить эту проблему, все обновляемые состояния и вложенные состояния обязательно должны быть ссылкой на новый объект.

Предлагаю переписать следующее:

const feedbackSchema = (state = [....], action) => {
  if (action.type === 'SET_VALUE') {
    return state.map((feedback) => { // <-- array.map creates new array reference
      if (feedback.key === action.payload[0]) {
        // Updating this feedback, create new object reference
        return {
          ...feedback,
          value: action.payload[1],
        };
      }

      // Not updating feedback, return current feedback reference
      return feedback;
    })
  }

  // Just return current state
  return state;
}

Однако более традиционно использовать операторы switch в функциях редуктора, поэтому их также можно переписать в более стандартном шаблоне/форме:

const feedbackSchema = (state = [....], action) => {
  const { type, payload } = action;

  switch(type) {
    case 'SET_VALUE':
      const [key, value] = payload;
      return state.map((feedback) => feedback.key === key
        ? { ...feedback, value }
        : feedback
      );

    default:
      return state;
  }
}

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