首页 > 解决方案 > 使 React 组件在商店中更改状态时重新渲染

问题描述

我有一个包含歌曲列表的播放列表组件。OnComponentDidMount 它从 axios 获取数据并使用操作 GET_PLAYLIST 将其填充到存储中的列表中。

有一个“添加到播放列表”按钮调用 UPDATE_PLAYLIST 操作,该操作应将选定的歌曲添加到存储中的播放列表列表,然后 axios 发布调用将新歌曲添加到 firebase 数据库。

当状态改变时播放列表组件应该更新,我不知道为什么它不是?

我的 Playlist.js 组件:

import React, { Component } from "react";
import { connect } from "react-redux";
import Auxilliary from "../../hoc/Auxilliary";
import PlayListItem from "../../components/PlayListItem/PlayListItem";

import classes from "./Playlist.css";
import * as actionTypes from "../../store/actions";

import axios from "../../hoc/axios-Firebase";

class Playlist extends Component {
  componentDidMount() {
    const searchQuery = "/playlist.json";
    axios
      .get(searchQuery)
      .then(response => {
        const playlistSongs = [];
        for (let song in response.data) {
          playlistSongs.push({
            artistName: response.data[song].artistName,
            trackName: response.data[song].track,
            collectionName: response.data[song].collectionName
          });
        }
        this.props.onGetPlaylist(playlistSongs);
      })
      .catch(error => {
        console.log(error);
      });
  }

  render() {
    if (this.props.playlist.length > 0) {
      const results = this.props.playlist.map((playlistItem, index) => {
        return (
          <PlayListItem
            key={index}
            artist={playlistItem.artistName}
            track={playlistItem.trackName}
            album={playlistItem.collectionName}
          />
        );
      });
      return (
        <Auxilliary>
          <table className={classes.playlistTable}>
            <thead>
              <tr>
                <th>Track</th>
                <th>Album</th>
                <th>Artist</th>
              </tr>
            </thead>
            <tbody>{results}</tbody>
          </table>
        </Auxilliary>
      );
    } else {
      return null;
    }
  }
}

const mapStateToProps = state => {
  return {
    playlist: state.playlist
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onGetPlaylist: value =>
      dispatch({
        type: actionTypes.GET_PLAYLIST,
        playlist: value
      })
  };
};

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

带有添加到播放列表按钮的组件:

import React, { Component } from "react";
import { connect } from "react-redux";
import Auxilliary from "../../hoc/Auxilliary";
import * as actionTypes from "../../store/actions";

import axios from "../../hoc/axios-Firebase";

import classes from "./SongInfo.css";

class SongInfo extends Component {
  constructor(props) {
    super(props);
    this.addToPlaylistHandler = this.addToPlaylistHandler.bind(this);
  }

  addToPlaylistHandler(
    track,
    trackID,
    artistName,
    collectionName,
    kind,
    trackPrice
  ) {
    const data = {
      track: track,
      trackID: trackID,
      artistName: artistName,
      collectionName: collectionName,
      kind: kind,
      trackPrice: trackPrice
    };
    this.props.onUpdatePlaylist(data);
    axios
      .post("/playlist.json", data)
      .then(response => {
        console.log(response);
      })
      .catch(error => {
        console.log(error);
      });
  }

  render() {
    return (
      <Auxilliary>
        <table>
          <thead>
            <tr>
              <th>Track</th>
              <th>Track ID</th>
              <th>Artist Name</th>
              <th>Collection Name</th>
              <th>Kind</th>
              <th>Track Price</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>{this.props.track}</td>
              <td>{this.props.id}</td>
              <td>{this.props.artistName}</td>
              <td>{this.props.collectionName}</td>
              <td>{this.props.kind}</td>
              <td>{this.props.trackPrice}</td>
            </tr>
          </tbody>
        </table>
        <button
          className={classes.addToPlaylist}
          onClick={this.addToPlaylistHandler(
            this.props.track,
            this.props.id,
            this.props.artistName,
            this.props.collectionName,
            this.props.kind,
            this.props.trackPrice
          )}
        >
          Add to Playlist
        </button>
      </Auxilliary>
    );
  }
}

const mapStateToProps = state => {
  return {
    playlist: state.playlist
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onUpdatePlaylist: value =>
      dispatch({
        type: actionTypes.UPDATE_PLAYLIST,
        playlistItem: value
      })
  };
};

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

我的减速机:

import * as actionTypes from "./actions";

const initialState = {
  searchValue: "",
  finalSearchValue: "",
  searchResults: [],
  sliceStart: 0,
  sliceEnd: 25,
  nextDisabled: false,
  prevDisabled: true,
  searchResult: {
    id: null,
    artistId: null,
    artistName: "",
    track: "",
    collectionName: "",
    kind: "",
    trackPrice: null
  },
  playlist: []
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case actionTypes.UPDATE_INPUT_VALUE:
      return {
        ...state,
        searchValue: action.searchValue
      };
    case actionTypes.SUBMIT_QUERY:
      return {
        ...state,
        finalSearchValue: action.finalSearchValue
      };
    case actionTypes.SET_SEARCH_RESULTS:
      return {
        ...state,
        searchResults: action.searchResults
      };
    case actionTypes.GET_PLAYLIST:
      return {
        ...state,
        playlist: action.playlist
      };
    case actionTypes.UPDATE_PLAYLIST:
      const newPlaylist = state.playlist;
      newPlaylist.push(action.playlistItem);
      return {
        ...state,
        playlist: newPlaylist
      };
    case actionTypes.SET_INFO_SEARCH_RESULT:
      return {
        ...state,
        searchResult: {
          id: action.searchResult.id,
          artistId: action.searchResult.artistId,
          artistName: action.searchResult.artistName,
          track: action.searchResult.track,
          collectionName: action.searchResult.collectionName,
          kind: action.searchResult.kind,
          trackPrice: action.searchResult.trackPrice
        }
      };
    case actionTypes.GET_PREV_25:
      if (state.sliceStart - 25 >= 0) {
        const newSliceStart = state.sliceStart - 25;
        const newSliceEnd = state.sliceEnd - 25;
        state.sliceStart = newSliceStart;
        state.sliceEnd = newSliceEnd;
        state.nextDisabled = false;
        if (0 <= newSliceStart) {
          state.prevDisabled = true;
        }
      }
      return {
        ...state,
        sliceStart: state.sliceStart,
        sliceEnd: state.sliceEnd,
        prevDisabled: state.prevDisabled,
        nextDisabled: state.nextDisabled
      };
    case actionTypes.GET_NEXT_25:
      if (state.sliceEnd < state.searchResults.length) {
        const newSliceStart = state.sliceStart + 25;
        const newSliceEnd = state.sliceEnd + 25;
        state.sliceStart = newSliceStart;
        state.sliceEnd = newSliceEnd;
        state.prevDisabled = false;
        if (state.searchResults.length === newSliceEnd) {
          state.nextDisabled = true;
        }
      }
      return {
        ...state,
        sliceStart: state.sliceStart,
        sliceEnd: state.sliceEnd,
        prevDisabled: state.prevDisabled,
        nextDisabled: state.nextDisabled
      };
    default:
      return state;
  }
};

export default reducer;

标签: reactjsreduxreact-redux

解决方案


一个问题可能是您正在直接修改状态,请尝试以下操作:

 case actionTypes.UPDATE_PLAYLIST:
   return {
     ...state,
     playlist: [...state.playlist, action.playlistItem]
   };

此外,您没有正确调用该操作,您需要将其包装到一个函数中:

<button
      className={classes.addToPlaylist}
      onClick={() => this.addToPlaylistHandler( // onClick expects a function and not its result
        this.props.track,
        this.props.id,
        this.props.artistName,
        this.props.collectionName,
        this.props.kind,
        this.props.trackPrice
      )}
    >

顺便说一句,您实际上并不需要在这里将所有这些道具作为参数传递,在内部访问它们更容易addToPlaylistHandler


推荐阅读