【问题标题】:React Redux updates state, but no re-renders, and props is undefined in render(){}React Redux 更新状态,但没有重新渲染,并且 props 在 render(){} 中未定义
【发布时间】:2021-02-09 04:40:27
【问题描述】:

所以我正在尝试在 react-redux 商店中设置一个计时器。我的操作正在发送到减速器,如果我从减速器中获取 console.log,我可以在控制台中看到它。

我什至在启动时间和停止计时器的按钮工作的地方连接了道具,因为我在控制台中每秒都会看到“TICK”动作。然而,我目前最大的困惑是,即使我把它挂在商店里,我似乎也看不到我的组件中呈现的值。 this.propscomponentDidMount and render(){} 上未定义。但是,同一组件上的按钮<button onClick={this.props.startTimer}>Start</button> 会触发 startTimer 操作,因此它正在更新状态,那么为什么组件没有反映它,为什么我的 this.props 值在组件中未定义?哦,是的,只是添加我的另外两个 redux reducer/actions 工作得很好,即使他们正在使用 thunk 中间件进行 fetch/promise。欢迎任何帮助/建议,因为这是我的应用程序的最后一个障碍

代码如下:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";

import thunk from 'redux-thunk';
import reducers from './reducers'

export const store = createStore(reducers, applyMiddleware(thunk));

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

减速器

export default (state = [], action) => {
  
  switch (action.type) {
    case "FORECAST_WEATHER":
      return action.payload;
    default: return state;
  }
};

export default (state = [], action) => {

  switch (action.type) {
    case "CURRENT_WEATHER":
      return action.payload;
    default: return state;
  }
};
const START_TIMER = "START_TIMER";
const STOP_IT = "STOP_IT";
const RESUME_IT = "RESUME_IT";
const TICK = "TICK";

const initialState = {
  timerOn: true,
  timerStart: 0,
  timerTime: 30
};

export default (state = initialState, action) => {
  
  console.log("state is ", state);
  console.log("action is ", action);

  switch (action.type) {
    case START_TIMER:
      return {
        ...state,
        timerOn: true,
        timerTime: state.timerTime,
        timerStart: Date.now() - state.timerTime,
        timerId: action.timerId
      };
    case STOP_IT: {
      return {
        ...state,
        timerOn: false,
        timerStart: 0
      };
    }
    case TICK:
      return { ...state, timerTime: state.timerTime + 1 };
    default:
      return state;
  }
};

import { combineReducers } from "redux";
import currentWeatherReducer from './currentWeatherReducer';
import forecastReducer from './forecastReducer';
import currentTimeReducer from './currentTimeReducer';

export default combineReducers({currentWeather: currentWeatherReducer, forecast: forecastReducer, currentTime: currentTimeReducer});

动作

import { getForecast } from "../api/GET/getForecast";
import { getCurrentWeather } from "../api/GET/getCurrentWeather";

export const fetchCurrentWeather = () => async (dispatch, getState) => {
  const { name, dt, main } = await getCurrentWeather();

  const cityProperties = { name, dt, main };

  dispatch({ type: "CURRENT_WEATHER", payload: cityProperties });
};

export const fetchForecasts = () => async (dispatch) => {
  const { list } = await getForecast();

  dispatch({ type: "FORECAST_WEATHER", payload: list });
};

定时器组件

import React, { useState, useEffect } from "react";
import "./Timer.scss";
import { connect } from "react-redux";
import { start, stop, tick } from "../../actions";

class Timer extends React.Component {
  constructor(props) {
    super(props);
  }

  handleStop = () => {
    clearInterval(this.props.timerId);
    this.props.stopTimer();
  };

  componentDidMount() {
    console.log("TIMER PROPS IS UNDEFINED HERE", this.props);
  }

  render() {
    const { timerTime } = this.props;

    console.log("PROPS ARE ALSO UNDEFINED HERE", this.props);

    return (
      <div className="timer-container">
        <button onClick={this.props.startTimer}>Start</button>
        TIME IS: {timerTime}
        <button onClick={this.handleStop}>Stop</button>
        <button onClick={this.props.resetTimer}>Reset</button>
      </div>
    );
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    timerOn: state.timerOn,
    timerStart: state.timerStart,
    timerTime: state.timerTime,
    timerId: state.timerId,
  };
};

const mapDispatchToProps = (dispatch) => {
  console.log("dispatch", dispatch);
  let timerId;

  return {
    startTimer: () => {
      timerId = setInterval(() => dispatch({ type: "TICK" }), 1000);
      dispatch({ type: "START_TIMER", timerId });
    },
    stopTimer: () => {
      dispatch({ type: "STOP_IT" });
      clearInterval(timerId);
    },
    resetTimer: () => dispatch({ type: "RESUME_IT" }),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Timer);

连接到商店的其他组件按预期工作

import React from "react";
import "./Header.scss";

import Timer from "../Timer";
import { connect } from "react-redux";
import { fetchCurrentWeather } from "../../actions";

class Header extends React.Component {
  componentDidMount() {
    this.props.fetchCurrentWeather();
    console.log(this.props.currentWeather);
  }

  render() {
    const { name, dt, temp } = this.props.currentWeather
    return (
      <div className="top-header">
        <div className="current-city info">
          <h1>{name}</h1>
          <div className="time-container">
            <i className="time-icon icon" >xxx</i>
            <span className="time-text">{dt}</span>
            <i className="time-icon icon" >xxx</i>
          </div>
        <span className="degrees">{temp}&#176;</span>
        </div>

        <Timer />
      </div>
    );
  }
}

const mapStateToProps = (state, ownProps) => {
  return { currentWeather: state.currentWeather };
};

export default connect(mapStateToProps, { fetchCurrentWeather })(Header);

另一个连接到商店的组件可以工作

import React, { useState, useEffect } from "react";
import "./WeatherCard.scss";
import {fetchForecasts} from '../../actions';

import {connect} from 'react-redux';

class WeatherCard extends React.Component {
  constructor(props) {
    super(props);

  }

  componentDidMount(){
    this.props.fetchForecasts();

    console.log('PROPS IS DEFINED HERE', this.props);
  }

  render() {
    const allRelevantData = Object.entries(this.props.forecast).map(([key, value]) => {
      const dateTime = new Date(value.dt * 1000);
      const day = dateTime.toString().slice(0, 3);

      const item = {
        day: day,
        temp: Math.round(value.main.temp),
        weatherMetaData: value.weather[0],
      };

      return item;
    });

    const uniqueForecasts = Array.from(
      new Set(allRelevantData.map((a) => a.day))
    ).map((day) => {
      return allRelevantData.find((a) => a.day === day);
    });

    return uniqueForecasts.map(({ day, temp, weatherMetaData }, index) => {
      if (index < 5) {
        return (
          <div className="weather-card" key={index}>
            <div className="day-temperature-container">
              <span className="day">{day}</span>
              <span className="temperature fade-in">{temp}&#176;</span>
            </div>
            <div className="weather-description">
              <span
                className="icon weather"
                style={{
                  background: `url(http://openweathermap.org/img/wn/${weatherMetaData.icon}.png)`,
                }}
              />
              <p>{weatherMetaData.description}</p>
            </div>
          </div>
        );
      }
    });
  }
}

const mapStateToProps = (state, ownProps) => {
  return {forecast: state.forecast};
};

export default connect(mapStateToProps, {fetchForecasts})(WeatherCard);

【问题讨论】:

    标签: javascript reactjs redux react-redux react-props


    【解决方案1】:

    听起来你在从 redux reducer 状态拉出时遇到了麻烦。

    通常我会,

    1. 在我的组件中,我将设置一个 useEffect() 或 componentDidMount 来启动我的 setInterval() 函数。如果您以秒为单位计时,则设置的 Interval 函数将每 1000 毫秒运行一次。

    2.) 在我的 setInterval 函数中,我将调度一个动作以在间隔的每次迭代期间增加减速器的初始状态值 0++ (currentValue++)。

    3.) 在我的组件中,我会将道具传递给组件。请务必检查值是否需要映射或直接传入而不使用 .map 函数。

    我也推荐使用Moment.js。您可以使用该库轻松完成所有这些工作。

    【讨论】:

      【解决方案2】:

      您需要从用减速器塑造的状态树中提取状态。你有三个减速器。 在 Timer.js 文件中,您需要从 currentTime 减速器形成的状态中提取状态。 如果你在Timer.js中改变ma​​pStateToProps,如下;你可以得到状态:

      Timer.js

      ...
      //pulling of states from the state tree that shaped with currentTime reducer
      const mapStateToProps = (state) => {
        return {
          timerOn: state.currentTime.timerOn,
          timerStart: state.currentTime.timerStart,
          timerTime: state.currentTime.timerTime,
          timerId: state.currentTime.timerId,
        };
      };
      

      不相关,但我只是想推广使用 redux-logger :) reducer.js

      ...
      //Create Store 
      
      // import { createLogger } from 'redux-logger';
      const loggerMiddleware = createLogger();
      
      const rootReducer = combineReducers({
          currentWeather: currentWeatherReducer,
          forecast: forecastReducer,
          currentTime: currentTimeReducer
      })
      
      export const store = createStore(
          rootReducer,
          applyMiddleware(
              thunkMiddleware,
              loggerMiddleware
          )
      );
      

      【讨论】:

      • 该死的,我需要多睡觉哈哈。但是感谢 loggerMiddleware,太棒了!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-31
      相关资源
      最近更新 更多