带有 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。
我正在尝试将一个元素添加到我的 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。