Redux с Immer не обновляет компонент - PullRequest
0 голосов
/ 17 февраля 2019

Я пытаюсь добавить элемент в массив в объекте в моем магазине Redux.Я вижу, что объект добавлен в магазин, но он не обновляет компонент.Если я покину страницу и вернусь, она появится.

Я почти уверен, что это проблема с мутацией состояния, но я не могу понять, в чем причина, если не пойму, что делает Immer.В компоненте я использую продукты для добавления строки в массив, передачи нового объекта моему редуктору и использования продукта для добавления этого объекта в массив этих объектов.

Я просмотрел тоннуиз подобных вопросов, которые все относятся к мутации состояния, но, как я понимаю, возврат от вызова компонента производить должен быть полностью новым объектом.Затем в редукторе вызов для получения должен возвращать новый массив объектов.

Впервые Immer используется в большом проекте, поэтому вполне возможно, что я не совсем понимаю, как он работает, это волшебство.

Компонент

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

Редуктор

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;

Любая помощь с благодарностью!Более подробная информация доступна при необходимости

...