使React组件根据商店中更改的状态重新渲染

时间:2019-10-01 09:27:27

标签: reactjs redux react-redux

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

有一个“添加到播放列表”按钮,该按钮调用UPDATE_PLAYLIST操作,该操作应将选定的歌曲添加到商店中的播放列表列表中,然后通过axios post调用将新歌曲添加到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;

2 个答案:

答案 0 :(得分:3)

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

 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内部访问它们。

答案 1 :(得分:0)

您在按钮上的onClick处理函数应该具有对处理函数的引用,但是当前您正在调用它,并且onClick具有该方法返回的值(在这种情况下,{{1} }返回addToPlaylistHandler) 要解决此问题,您需要将onClick更改为一个内联函数,或者将您的处理程序包装到另一个返回函数中,从而携带数据。

方法1:使onClick成为嵌入式函数

undefined

方法2:重写您的addToPlaylistHandler以在其中携带参数

<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>

如果您的开发环境支持类属性语法,则可以缩短处理程序代码(无需在构造函数中绑定处理程序:

addToPlaylistHandler(
  track,
  trackID,
  artistName,
  collectionName,
  kind,
  trackPrice
) {
  return () => {
    // this one will be executed on click
    const data = {
      track: track,
      trackID: trackID,
      artistName: artistName,
      collectionName: collectionName,
      kind: kind,
      trackPrice: trackPrice
    };
    axios
      .post("/playlist.json", data)
      .then(response => {
        // Ideally, you should update the state on sucessful post
        this.props.onUpdatePlaylist(data);
        console.log(response);
      })
      .catch(error => {
        // and handle the error properly
        console.log(error);
      });
    }
}