Reputation: 589
I am making a project using React and Redux. I am updating a reducer with an array and then mapping that array into a component. The component has a state which is used to conditionally render a an image. I want this components state to be reset to its default whenever the reducers state is updated.
Here is the reducer :
const reducer = (state={board : []}, action) => {
switch (action.type) {
case 'MAKE_BOARD':
return Object.assign({}, state, {
board: action.payload
})
default:
return state
}
}
Here is the App.js page which calls the reducer:
import React, { Component } from 'react';
import './App.css';
import Board from '../Board/Board'
import {connect} from 'react-redux'
const mapReduxStateToProps= (reduxState) => ({
reduxState
})
class App extends Component {
state = {
size : '8',
squareArray : []
}
handleChange =(event) => {
this.setState({
...this.state,
size : Number(event.target.value)
})
console.log(this.state)
}
//This function makes a an array of numbers with 1/4 'X's and 3/4 'O's
boardMaker = (number) => {
this.setState({squareArray:[]});
let size = number*number;
let placeHolderArray = []
for(let i=0; i<size; i++){
placeHolderArray.push('O')
}
for(let j=0; j<size/4;j++){
placeHolderArray[Math.floor(Math.random()*size)] = 'X'
}
this.setState({squareArray: placeHolderArray})
console.log(placeHolderArray)
console.log(this.state.squareArray);
this.props.dispatch({type:'MAKE_BOARD', payload: placeHolderArray})
this.props.dispatch({type: 'SET_SIZE', payload : this.state.size})
}
render() {
return (
<div className="App">
<header className="App-header">
<input onChange={this.handleChange} placeholder='Size'/>
<button onClick={()=>this.boardMaker(this.state.size)}>Make Board</button>
<div className='board' style={{width: 40*this.props.reduxState.size.size}}>
{/* {this.state.squareArray.map(space => {
return(
<div className='square'>{space}</div>
)
})} */}
{JSON.stringify(this.props.reduxState)}
{this.props.reduxState.reducer.board.map((space,index) =>
<Board keys={index} id={space}/>
)
}
</div>
</header>
</div>
);
}
}
export default connect(mapReduxStateToProps)(App);
Here is the board.js where the reducer is being mapped:
import React, { Component } from 'react';
import './Board.css'
import { connect } from 'react-redux';
const mapReduxStateToProps = (reduxState) => ({reduxState})
class Board extends Component {
state = {
clicked: false,
displayFlag: false,
counter: 0,
}
imageDisplay= () => {
if(!this.state.clicked && !this.state.displayFlag){
return <img key={this.props.id} src='images/Frog-1.png' alt='Not Clicked'/>
} else if(this.state.displayFlag){
return <img src='images/Yellow.png' alt='None' />
} else {
return this.state.counter;
}
}
handleMouseDown = e => {
document.oncontextmenu = function() {
return false;
}
e = e || window.event;
//console.log(e.which)
console.log(this.state)
switch(e.which) {
case 1 : this.showNumber(); break;
case 2 : break;
case 3 : this.displayFlag(); return false;
default: break;
}
}
displayFlag= () => {
console.log('running')
this.setState({...this.state, displayFlag : !this.state.displayFlag })
return this.state.displayFlag;
}
showNumber= () => {
console.log('run')
let Xcounter = 0;
let edge = Math.sqrt(this.props.reduxState.reducer.board.length)
console.log(edge)
let keys = this.props.keys
let board = this.props.reduxState.reducer.board
let minX = keys%edge === 0 ? 0 : -1;
let maxX = keys%edge === (edge-1) ? 0 : 1;
let minY = Math.floor(keys/edge) == 0 ? 0 : -1;
let maxY = Math.floor(keys/edge) == (edge-1) ? 0 : 1;
for(let x = minX; x <= maxX; x++){
for(let y = minY; y<=maxY; y++){
if(board[keys+x+(y*edge)]=== 'X'){
Xcounter++
}
}
}
if(this.props.id === 'X'){
this.setState({...this.state, clicked: true, counter: 'X'})
return this.state.counter;
}
this.setState({...this.state, clicked: true, counter: Xcounter})
return this.state.counter;
}
render() {
return (
<div className="App">
<div onMouseDown={()=>this.handleMouseDown()} className='square'>{this.imageDisplay()}</div>
</div>
);
}
}
export default connect(mapReduxStateToProps)(Board);
I want the local state on Board to reset when the reducer is updated. I can probably do this be adding properties to the reducer and using them in the component but I am hoping there is a better method.
Upvotes: 1
Views: 2217
Reputation: 4547
Looking at your use case - you need to empty the local state on every MAKE_BOARD
action.
Here are two approaches that I had in mind -
componentWillReceiveProps
/ getDerivedStateFromProps
/ componentDidUpdate
and empty your local state.RESET_ACTION_NAME
which returns your initial state ( which is empty ) on every update. But for it to function correctly, you'll need to put your local state as an object in the redux store.PS: If you know that your action to reset the state only has a limited scope, you needn't put your state in the store. But, if you know that this action spans across multiple components, you can consider transforming the local state into a redux store object.
Upvotes: 2
Reputation: 81126
This gets a bit into design opinion land, but I think the most straightforward way to deal with this is to put the board's state in redux as well rather than local state (probably in its own separate reducer in the same store), because (if I understand correctly) you're really saying that you want its state to change (reset) based on the 'MAKE_BOARD' action.
Another alternative would be to include an integer board id in your redux state that you increment each time you remake the board. Then if you use that as the “key” property of your board I believe that will cause it to automatically reset because it will unmount the previous board and create a new one.
If you want more implementation specifics about how to go about this, I recommend getting a version of the code into codepen or codesandbox and sharing that in your question as well (but it's good that you're including most of the relevant code directly in your question text).
Upvotes: 0