带有 Immer 的 Redux 不更新组件

Redux with Immer not updating component

我正在尝试将一个元素添加到我的 Redux 存储中对象的数组中。我看到对象已添加到商店,但它没有更新组件。如果我离开页面并且 return 它会出现。

我很确定这是一个状态突变问题,但我无法弄清楚我哪里出错了,除非我从根本上误解了 Immer 在做什么。在我使用 produce 将字符串添加到数组的组件中,将新对象传递到我的 reducer 并使用 produce 将该对象添加到这些对象的数组中。

我已经查看了大量与状态突变相关的类似问题,但我的理解方式是组件调用 produce 的 return 应该是一个全新的对象。然后在 reducer 中对 produce 的调用应该是 returning 一个新的对象数组。

这是第一次在大型项目中使用 Immer,所以我完全有可能不完全了解它的工作原理。

Component

import produce              from 'immer';
import React, { Component } from 'react';
import { connect }          from 'react-redux';
import { Link }             from 'react-router-dom';
import uuid                 from 'uuid/v4';
import { generate }         from 'generate-password';

import { updateLeague } from '../../actions/leagues';
import { addTeam }      from '../../actions/teams';
import { addUser }      from '../../actions/users';
import Team             from '../../classes/Team';
import User             from '../../classes/User';
import UserWidget       from '../utils/user/UserWidget';

class ViewLeague extends Component {
  state = {
    league     : null,
    isOwner    : false,
    owner      : '',
    teams      : [],
    inviteEmail: ''
  };

  componentWillMount() {
    console.log('mount props', this.props.leagues);
    const { leagues, uuid, leagueId, users, teams } = this.props;
    if (leagues.length > 0) {
      const league      = leagues.find(league => league.uuid === leagueId);
      const owner       = users.find(user => league.leagueManager === user.uuid);
      const leagueTeams = teams.filter(team => league.teams.includes(team.uuid));
      this.setState({
        league,
        isOwner: league.leagueManager === uuid,
        owner,
        teams  : leagueTeams
      });
    }
  }

  handleUpdate(event, fieldName) {
    this.setState({ [ fieldName ]: event.target.value });
  }

  findUserByEmail(email) {
    //Todo if not found here hit server
    return this.props.users.find(user => user.email === email);
  }

  sendInvite = () => {
    const { addTeam, addUser, updateLeague } = this.props;
    const { league }                         = this.state;
    const newManager                         = this.findUserByEmail(this.state.inviteEmail);
    const newTeamUuid                        = uuid();
    let newLeague                            = {};
    if (newManager) {
      const newTeam = new Team('New Team', newManager.uuid, newTeamUuid);
      addTeam(newTeam);
    } else {
      const newPass     = generate({
        length   : 10,
        number   : true,
        uppercase: true,
        strict   : true
      });
      const newUserUuid = uuid();
      const newUser     = new User('', this.state.inviteEmail, newPass, '', '', newUserUuid);
      addUser(newUser);
      const newTeam = new Team('New Team', newUserUuid, newTeamUuid);
      addTeam(newTeam);
      newLeague = produce(league, draft => {draft.teams.push(newTeamUuid);});
      updateLeague(newLeague);
      console.log('invite props', this.props);
      console.log('league same', league === newLeague);
    }

    //Todo handle sending email invite send password and link to new team
    console.log('Invite a friend', this.state.inviteEmail);
  };

  renderInvite() {
    const { isOwner, league, teams } = this.state;
    if (isOwner) {
      if ((league.leagueType === 'draft' && teams.length < 8) || league.leagueType !== 'draft') {
        return (
            <div>
              <p>You have an empty team slot. Invite a fried to join!</p>
              <input type="text"
                     placeholder={'email'}
                     onChange={() => this.handleUpdate(event, 'inviteEmail')}/>
              <button onClick={this.sendInvite}>Invite</button>
            </div>
        );
      }
    }
  }


  renderViewLeague() {
    console.log('render props', this.props.leagues);
    const { league, owner, teams } = this.state;
    const editLink                 = this.state.isOwner ?
        <Link to={`/leagues/edit/${this.props.leagueId}`}>Edit</Link> :
        '';
    return (
        <div>
          <h2>{league.leagueName} </h2>
          <h3>League Manager: <UserWidget user={owner}/> - {editLink}</h3>
          <p>League Type: {league.leagueType}</p>
          {this.renderInvite()}
          <br/>
          <hr/>
          <h2>Teams</h2>
          <span>{teams.map((team) => (<p key={team.uuid}>{team.teamName}</p>))}</span>
          <span>
            <h2>Scoring:  </h2>
            {league.scoring.map((score, index) => (
                    <p key={index}>{`Round ${index + 1}: ${score} points`}</p>
                )
            )}
          </span>
        </div>
    );
  }


  render() {
    if (!this.state.league) {
      return (
          <div>
            <h2>No league Found</h2>
          </div>
      );
    } else {
      return (
          <div>
            {this.renderViewLeague()}
          </div>
      );
    }

  }
}

export default connect(
    ({ leagues: { leagues }, teams: { teams }, users: { users }, auth: { uuid } },
     { match: { params: { leagueId } } }) => ({
      leagues,
      teams,
      users,
      uuid,
      leagueId
    }), ({
      addTeam     : (team) => addTeam(team),
      addUser     : (user) => addUser(user),
      updateLeague: (league) => updateLeague(league)
    })
)(ViewLeague);

Reducer

import produce from 'immer';

import {
  ADD_LEAGUE,
  UPDATE_LEAGUE
} from '../actions/types';

const DEFAULT_LEAGUES = {
  leagues: [ {
    leagueName   : 'Test League',
    leagueManager: 'testUser12345',
    uuid         : 'testLeague12345',
    teams        : [ 'testTeam12345', 'testTeam23456' ],
    scoring      : [ 25, 20, 15, 10, 5, -5 ],
    leagueType   : 'draft'
  } ]
};

const leaguesReducer = (state = DEFAULT_LEAGUES, action) =>
    produce(state, draft => {
      // noinspection FallThroughInSwitchStatementJS
      switch (action.type) {
        case ADD_LEAGUE:
          draft.leagues.push(action.league);
        case UPDATE_LEAGUE:
          console.log('updating league', action.league);
          const { league }  = action;
          const leagueIndex = draft.leagues.findIndex(fLeague => league.uuid === fLeague.uuid);
          draft.leagues.splice(leagueIndex, 1, league);
      }
    });

export default leaguesReducer;

非常感谢任何帮助!!如果需要,可提供更多信息

尝试在案例块的末尾添加 return;

您可以阅读有关从生产者返回数据的更多信息,并查看该做什么和不该做什么的示例here