更新数组(在对象内部)时,通过向其添加对象,不会重新呈现子组件。但是,父组件是。
我尝试更新对象的非数组属性,同时还更新了对象的array属性,然后子组件将更新。例如:
不起作用:
obj.arr.push(user);
作品:
obj.arr.push(user);
obj.test = "wow";
我的问题是users
道具从Users
组件传递到Lobby
组件。当用户加入时,套接字事件lobby_player_joined
被触发,从而修改了users
数组。
大厅组件(父级):
...
const StyledTabs = styled(Tabs)`${TabsStyle};`;
class Lobby extends Component {
constructor(props) {
super(props);
this.state = {
tab: 0,
};
this.props.setTitle('Lobby');
}
static get propTypes() {
return {
history: PropTypes.shape({ push: PropTypes.func.isRequired }).isRequired,
location: PropTypes.shape({ state: PropTypes.object }).isRequired,
setTitle: PropTypes.func.isRequired,
initializeSocket: PropTypes.func.isRequired,
onceSocketMessage: PropTypes.func.isRequired,
onSocketMessage: PropTypes.func.isRequired,
sendSocketMessage: PropTypes.func.isRequired,
};
}
async componentDidMount() {
await this.props.initializeSocket((error) => {
console.error(error);
});
await this.props.onSocketMessage('exception', (error) => {
console.log(error);
});
await this.props.onceSocketMessage('lobby_joined', (lobby) => {
this.setState({ lobby });
});
await this.props.sendSocketMessage('lobby_join', {
id: this.props.location.state.id,
password: this.props.location.state.password,
});
await this.props.onSocketMessage('lobby_player_joined', (user) => {
const { lobby } = this.state;
lobby.users.push(user);
return this.setState({ lobby });
});
await this.props.onSocketMessage('lobby_player_left', (user) => {
const { lobby } = this.state;
const userIndex = lobby.users.findIndex(u => u.id === user.id);
if (userIndex !== -1) {
lobby.users.splice(userIndex, 1);
this.setState({ lobby });
}
});
await this.props.onSocketMessage('lobby_new_host', (host) => {
const { lobby } = this.state;
lobby.host = host;
return this.setState({ lobby });
});
}
handleTab = (event, value) => {
console.log(this.state.lobby);
this.setState({ tab: value });
};
handleSwipe = (value) => {
this.setState({ tab: value });
};
render() {
if (!this.state.lobby) {
return (<div> Loading... </div>);
}
return (
<Container>
<AppBar position="static">
<StyledTabs
classes={{
indicator: 'indicator-color',
}}
value={this.state.tab}
onChange={this.handleTab}
fullWidth
centered
>
<Tab label="Users" />
<Tab label="Info" />
</StyledTabs>
</AppBar>
<SwipeableViews
style={{ height: 'calc(100% - 48px)' }}
containerStyle={{ height: '100%' }}
index={this.state.tab}
onChangeIndex={this.handleSwipe}
>
<TabContainer>
<Users
{...this.state.lobby}
/>
</TabContainer>
<TabContainer>
<Info
{...this.state.lobby}
/>
</TabContainer>
</SwipeableViews>
</Container>
);
}
}
...
用户组件(子):
...
class Users extends Component {
state = {
isReady: false,
usersReady: [],
};
async componentDidMount() {
await this.props.onSocketMessage('lobby_user_ready', (data) => {
this.setState(prevState => ({
usersReady: [...prevState.usersReady, data.socketId],
}));
});
await this.props.onSocketMessage('lobby_user_unready', (data) => {
this.setState(prevState => ({
usersReady: prevState.usersReady.filter(id => id !== data.socketId),
}));
});
}
componentWillUnmount() {
this.props.offSocketMessage('lobby_user_ready');
this.props.offSocketMessage('lobby_user_unready');
}
static get propTypes() {
return {
id: PropTypes.number.isRequired,
users: PropTypes.arrayOf(PropTypes.object).isRequired,
userCount: PropTypes.number.isRequired,
host: PropTypes.shape({
username: PropTypes.string.isRequired,
}).isRequired,
sendSocketMessage: PropTypes.func.isRequired,
onSocketMessage: PropTypes.func.isRequired,
offSocketMessage: PropTypes.func.isRequired,
};
}
readyChange = () => {
this.setState(prevState => ({ isReady: !prevState.isReady }), () => {
if (this.state.isReady) {
return this.props.sendSocketMessage('lobby_user_ready', { id: this.props.id });
}
return this.props.sendSocketMessage('lobby_user_unready', { id: this.props.id });
});
};
renderStar = (user) => {
const { host } = this.props;
if (host.username === user.username) {
return (<Icon>star</Icon>);
}
return null;
}
render() {
return (
<UserContainer>
{ this.props.users.length }
<CardsContainer>
{this.props.users.map(user => (
<UserBlock
className={this.state.usersReady.includes(user.socketId) ? 'flipped' : ''}
key={user.socketId}
>
<BlockContent className="face front">
{ this.renderStar(user) }
<div>{user.username}</div>
<Icon className="icon">
close
</Icon>
</BlockContent>
<BlockContent className="face back">
<Icon>
star
</Icon>
<div>{user.username}</div>
<Icon className="icon">
check
</Icon>
</BlockContent>
</UserBlock>
))}
</CardsContainer>
<InfoContainer>
<p>Players</p>
<p>
{this.props.users.length}
{' / '}
{this.props.userCount}
</p>
<p>Ready</p>
<p>
{this.state.usersReady.length}
{' / '}
{this.props.userCount}
</p>
</InfoContainer>
<StyledButton
variant={this.state.isReady ? 'outlined' : 'contained'}
color="primary"
onClick={this.readyChange}
>
{ this.state.isReady ? 'Unready' : 'ready'}
</StyledButton>
</UserContainer>
);
}
}
...
在修改数组道具时,有人可以帮我解决Users
组件的更新/重新渲染吗?
答案 0 :(得分:4)
不要改变状态。使用这样的东西
await this.props.onSocketMessage('lobby_player_joined', (user) => {
const { lobby } = this.state;
return this.setState({ lobby : {...lobby, users: lobby.users.concat(user)} });
});
编辑:修复了丢失的括号
答案 1 :(得分:1)
这是因为React会比较道具的相等性以确定是否重新渲染组件。代替
obj.arr.push(user);
尝试
const newObj = {...obj, arr: obj.arr.concat(user)};
这将创建一个新的对象。
一种替代方法是使用Immutable.js