问题
I have a react component with a Mobx store that looks like this:
class Board extends Component {
componentDidMount() {
this.props.dataStore.getSinglePlayer(1)
this.props.squareStore.getAllSquares()
}
componentWillReceiveProps(nextProps) {
if (
this.props.dataStore.currentPlayer !==
this.nextProps.dataStore.currentPlayer
) {
this.nextProps.dataStore.getSinglePlayer(1)
}
}
randomNumber() {
return Math.floor(Math.random() * 6) + 1
}
roll(playerId, playerPosition) {
let dice1 = this.randomNumber()
let dice2 = this.randomNumber()
let totalRoll = dice1 + dice2
let totalPosition = totalRoll + playerPosition
this.props.dataStore.changeUserPosition(playerId, totalRoll)
document.getElementById('dice').innerHTML =
'You rolled ' + totalRoll + '! You are now on ' + totalPosition
}
render() {
var player = {
name: '',
id: '',
position: 0
}
console.log(this.props.dataStore.currentPlayer)
if (this.props.dataStore.currentPlayer) {
var currentPlayer = this.props.dataStore.currentPlayer
player = {
name: currentPlayer.name,
id: currentPlayer.id,
position: currentPlayer.position
}
}
if (this.props.squareStore.squares) {
var squares = this.props.squareStore.squares.map((square, i) => {
var movement
if (i == player.position) {
movement = **
}
return (
<li key={i}>
<span>
{square.title}
<br />
{movement}
{square.price}
</span>
</li>
)
})
} else {
squares = <h4>Squares being loaded...</h4>
}
return (
<div>
<h1>Player: {player.name}</h1>
<h2>Roll Dice!</h2>
<button onClick={() => this.roll(player.id, player.position)}>
Roll
</button>{' '}
<h1 id="dice">{}</h1>
<div className="board">
<ul>{squares}</ul>
</div>
</div>
)
}
}
export default inject('dataStore', 'squareStore')(observer(Board))
When the button is clicked this.props.dataStore.currentPlayer.position updates with a new value. However this new value only shows up when the page is manually refreshed. Is there a way of telling the component that the value has changed and it should rerender automatically?
回答1:
React.Component Lifecycle => Updating
An update can be caused by changes to props or state.
That's mean if you would like to re-render your component, you need to:
- update Component state by using
this.setState({ })
method - or pass updated
props
to that component from parent - or if you are using
Redux/MobX
as the state manager, you need to update the store, so connectedprops
will be updated andre-render
would be triggered
回答2:
You should consider using state
so you setState
to re renders your component.
I Would suggest to refactor your code , especially your render
. I think your player should be the state as some params changes each click (current player id, name and position )..be aware not to directly set your state in render
cause this may cause infinite loop and render
should always remain pure method. So this need more serious refactor.
Moreover, usually there is no need to use stateful component where you don't have state or even a constructor!
Anyway here we just setState
the totalPosition and this MAY work for you, but you should consider to optimize your component .
class Board extends Component {
constructor(props){
super(props);
this.state = {
totalPosition: null
}
}
componentDidMount() {
this.props.dataStore.getSinglePlayer(1)
this.props.squareStore.getAllSquares()
}
componentWillReceiveProps(nextProps) {
if (
this.props.dataStore.currentPlayer !==
this.nextProps.dataStore.currentPlayer
) {
this.nextProps.dataStore.getSinglePlayer(1)
}
}
randomNumber() {
return Math.floor(Math.random() * 6) + 1
}
roll(playerId, playerPosition) {
let dice1 = this.randomNumber()
let dice2 = this.randomNumber()
let totalRoll = dice1 + dice2
this.setState({
totalPostion: totalRoll + playerPosition
});
let totalPosition = totalRoll + playerPosition
this.props.dataStore.changeUserPosition(playerId, totalRoll)
document.getElementById('dice').innerHTML =
'You rolled ' + totalRoll + '! You are now on ' + this.state.playerPostion
}
render() {
var player = {
name: '',
id: '',
position: 0
}
console.log(this.props.dataStore.currentPlayer)
if (this.props.dataStore.currentPlayer) {
var currentPlayer = this.props.dataStore.currentPlayer
player = {
name: currentPlayer.name,
id: currentPlayer.id,
position: currentPlayer.position
}
}
if (this.props.squareStore.squares) {
var squares = this.props.squareStore.squares.map((square, i) => {
var movement
if (i == player.position) {
movement = **
}
return (
<li key={i}>
<span>
{square.title}
<br />
{movement}
{square.price}
</span>
</li>
)
})
} else {
squares = <h4>Squares being loaded...</h4>
}
return (
<div>
<h1>Player: {player.name}</h1>
<h2>Roll Dice!</h2>
<button onClick={() => this.roll(player.id, player.position)}>
Roll
</button>{' '}
<h1 id="dice">{}</h1>
<div className="board">
<ul>{squares}</ul>
</div>
</div>
)
}
}
export default inject('dataStore', 'squareStore')(observer(Board))
回答3:
<button onClick={() => this.roll(player.id, player.position); this.forceUpdate()}>
回答4:
I believe that you are not quite setting this interaction up correctly. I've not used MobX, but the react way to do this would be to just change the props. If you had a wrapping component around this that was listening to changes on the store, and passing the data into Board, it would automatically update. I think this MobX connect package will be very useful for you.
来源:https://stackoverflow.com/questions/48607560/rerender-react-component-when-props-change