I'm currently working on a web based Battleship game for one my Odin Project assignments. At some point I felt that the mediator pattern would be the perfect choice to deal with firing missiles for the player and the CPU. Now, my assignment encourages me to test the game thoroughly without console.log but with Jest. I've been able to test some features of the game but the mediator pattern is confusing. Mocking functions or modules is likely to be the right direction to go for but to be honest I've read heaps of guides and I've not been able to implement them (understanding mocks has been hard as well). The function notifyAttackinside EventManager has already been tested the old way with console.log.
Can anyone let me know what I'm doing wrong please?
Event Manager
export {EventManager}
const EventManager = {
gameManager: GameManager,
notifyAttack(who, coordinate){
if(!who)
throw new Error(`Unknown player`);
else
who === `CPU` ? GameManager.player.board.getAttack(coordinate) : GameManager.cpu.board.getAttack(coordinate);
GameManager.turn = who;
}
}
Game Manager
import {Player} from "./player";
export {GameManager}
const GameManager = {
turn: undefined,
player: undefined,
cpu: Player(),
}
Player
import {coordinate, GameBoard} from './gameboard';
import { EventManager } from './eventmanager';
export {Player}
const playerActions = {
eventManager: EventManager,
fire(coordinate){
this.eventManager.notifyAttack(this.name, coordinate);
}
}
function Player(name){
const player = Object.create(playerActions);
player.board = GameBoard();
name === undefined ? player.name = `CPU`: player.name = name;
return player;
}
GameBoard
import { Ship } from "./ship"
export {GameBoard, coordinate, shipOrientation, tile}
function coordinate(x,y){
const boardSize = 10;
if(x > boardSize || x < 1)
throw new Error(`X coordinate is out of boundaries`);
if(y > boardSize || y < 1)
throw new Error(`Y coordinate is out of boundaries`);
return{x:x, y:y}
}
function tile(coordinate, id){
return{coordinate: coordinate, id: id}
}
const shipOrientation = {
HORIZONTAL: Symbol(`horizontal`),
VERTICAL: Symbol(`vertical`),
}
const gameboardActions = {
placeShips(shipType, orientation, inputCoordinate){
const ship = Ship(shipType);
ship.ID = `${inputCoordinate.x},${inputCoordinate.y}`;
this.tiles.forEach(tile=>{
if(tile.coordinate.x === inputCoordinate.x && tile.coordinate.y === inputCoordinate.y)
throw new Error(`There's already an object on that input coordinate`);
})
if(orientation === shipOrientation.HORIZONTAL){
if(inputCoordinate.x + ship.length > this.size)
throw new Error(`Part of ship is out of board X boundary`);
for(let i = 0; i<ship.length; ++i)
this.tiles.push(tile(coordinate(inputCoordinate.x+i, inputCoordinate.y), `${ship.ID}`));
}else if(orientation === shipOrientation.VERTICAL){
if(inputCoordinate.y + ship.length > this.size)
throw new Error(`Part of ship is out of board Y boundary`);
for(let i = 0; i<ship.length; ++i)
this.tiles.push(tile(coordinate(inputCoordinate.x, inputCoordinate.y+i), `${ship.ID}`));
}else
throw new Error(`Undefined ship orientation`);
this.shipsLog.set(`${ship.ID}`,ship);
},
getAttack(inputCoordinate){
let isShip, ID;
this.tiles.forEach(tile=>{
if(tile.coordinate.y===inputCoordinate.y&&tile.coordinate.x===inputCoordinate.x&&tile.id){
ID = tile.id;
return isShip = true;
}
})
if(isShip){
this.shipsLog.get(ID).hit()
if(this.shipsLog.get(ID).isSunk){
this.removeShip(ID);
this.checkSunkFleet();
}
}else
this.tiles.push(tile(inputCoordinate, undefined));
},
removeShip(ID){
this.shipsLog.delete(ID);
for(let i = 0; i<this.tiles.length; ++i)
if(this.tiles[i].id===ID)
this.tiles.splice(i,1);
},
checkSunkFleet(){
this.shipsLog.size === 0 ? this.sunkFleet=true:this.sunkFleet=false;
}
}
function GameBoard (){
const gameboard = Object.create(gameboardActions);
gameboard.shipsLog = new Map();
gameboard.tiles= [];
gameboard.size= 10;
gameboard.sunkFleet = false;
return gameboard;
}
Jest test
import {GameBoard, coordinate} from "./gameboard";
import {GameManager} from './gamemanager';
import {Player} from "./player";
import {EventManager} from "./eventmanager";
GameManager.player = Player(`Pablo`);
describe(`Player set up`, ()=>{
test(`Player's name is Pablo`,()=>{
expect(GameManager.player.name).toMatch(/^[A-Z]+$/i);
});
test(`Player has a board to play with`, ()=>{
expect(GameManager.player.board).toMatchObject(GameBoard());
});
})
describe(`Player's actions`,()=>{
test(`Pablo fires a missile, he misses ship target though`, ()=>{
const myCoordinate = coordinate(5,5);
const spy = jest.spyOn(EventManager, 'notifyAttack')
GameManager.player.fire(myCoordinate);
expect(spy).toBeCalled();
expect(GameManager.cpu.getAttack).toBeCalledWith(myCoordinate);
expect(GameManager.cpu.shipsLog.has(`${myCoordinate.x}, ${myCoordinate.y}`));
})
})
So the flow goes in this way:
A Player already set up in GameManager (Pablo) fires a missile by triggering fire() inside the Player object
fire() reports EventManager who fires the missile and its coordinates
EventManager calls CPU GameBoard getAttack() method that records Pablo's missing missile
You guys might wonder why I'm using an EventManager instead of relying on GameManager. Basically, GameManager is in charge of changing turns, set up the game whereas EventManager specifically deals with the battle to prevent coupling between Player and CPU
I'd like to hear from you if you need more details for the question.
Cheers!
Well I found the answer after some struggle. Mocks are not the best idea in this situation, instead jest spies let me check if functions are called without changing the implementation like mocks so I wasn't far from the answer.
Here you go jest test code for future reference
describe(`Player's attack`,()=>{
GameManager.cpu = Player();
GameManager.cpu.board.placeShips(shipType.DESTROYER, shipOrientation.HORIZONTAL, coordinate(4,4))
let myCoordinate = coordinate(5,5);
const eventManager = jest.spyOn(EventManager, 'notifyAttack');
const cpuGetsAttack = jest.spyOn(GameManager.cpu.board, 'getAttack')
test(`Pablo fires a missile, he misses his target ship though`, ()=>{
GameManager.player.fire(myCoordinate);
expect(eventManager).toBeCalled();
expect(cpuGetsAttack).toBeCalledWith(myCoordinate);
expect(GameManager.cpu.board.shipsLog.has(`${myCoordinate.x},${myCoordinate.y}`)).toBeFalsy();
expect(GameManager.cpu.board.tiles).toContainEqual(tile(myCoordinate, undefined));
});
test(`Pablo fires a missile and hits his target ship`, ()=>{
myCoordinate.x = 4;
myCoordinate.y = 4;
GameManager.player.fire(myCoordinate);
expect(eventManager).toBeCalled();
expect(cpuGetsAttack).toBeCalledWith(myCoordinate);
expect(GameManager.cpu.board.shipsLog.has(`${myCoordinate.x},${myCoordinate.y}`)).toBeTruthy();
expect(GameManager.cpu.board.tiles).toContainEqual(tile(myCoordinate, GameManager.cpu.id));
});
});
Related
I have a couple issues - the app is a vanilla javascript app for school that maps audio to each alphabetical key. The issues are as follows:
It takes quite a while to load the audio - the app proceeds like normal but with no audio - it usually can run as intended after a few minutes or a few refreshes, but I do want to get rid of that period. I tried to fix that using (document.readyState === "interactive") and if (allAudio.entries(audio => (audio.readyState === 4)))
It doesn't take the first key down event to change the intro slides, it takes the second - but I'm also not sure how to fix this. The slides are in an array that goes through each item and then makes them display as none. I also tried to add the event listener for the keyboard earlier, but to no avail.
To look at the bugs yourself, live link is here: https://haeuncreative.github.io/mosatic/
relevant code:
if (document.readyState === "interactive") {
const allAudio = document.querySelectorAll("audio")
console.log(allAudio)
if (allAudio.entries(audio => (audio.readyState === 4)))
window.addEventListener('load', function() {
const keysDown = new KeyDownHandler()
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
context.fillStyle = '#967bb6'
context.fillRect(0, 0, 1000, 562.5)
const clickDown = new ClickHandler(keysDown)
}
);
}
export default class KeyDownHandler {
constructor() {
this.addPressListener()
// audio
this.soundBank = new AudioBank
this.soundBank.createBank(CONSTANTS.KEY_ALPHABET)
// visual // intro
this.intro1 = document.querySelector('#intro1')
this.intro2a = document.querySelector('#intro2a')
this.intro2b = document.querySelector('#intro2b')
this.intro2c = document.querySelector('#intro2c')
this.intro3 = document.querySelector('#intro3')
this.intro4 = document.querySelector('#intro4')
this.introBank = [
this.intro1,
this.intro2a,
this.intro2b,
this.intro2c,
this.intro3,
this.intro4
]
// visual // main
this.body = document.querySelector('body')
this.canvas = document.querySelector('canvas');
this.context = this.canvas.getContext('2d');
this.background_colors = ["#95c88c", "#967bb6", "#A7C7E7", "#FF6961"]
this.aniBank = new AniBank;
// recording/user interaction
this.keys = [];
this.durations = [];
this.recording = false;
}
introSwitch() {
this.soundBank.playSpace()
if (this.currentSlide) {
this.currentSlide.style.animation = "fadeOut 1s"
this.currentSlide.style.display = "none"
this.currentSlide = ""
}
if (!this.introBank.length) {
slide.style.display = "none"
this.introFinish = true
}
if (this.introBank.length) {
let slide = (this.introBank.shift())
slide.style.filter = "brightness(60%)"
console.log(slide)
if (slide === this.intro4 || !slide) {
this.canvas.style.display = "flex";
this.addKeyListeners()
}
if (slide.style.display = "none") {
slide.style.display = "flex"
slide.style.filter = "brightness(60%)"
}
this.currentSlide = slide;
slide.style.filter = "brightness(100%)"
}
}
addPressListener() {
window.addEventListener('keypress', e => {
e.preventDefault()
e.stopImmediatePropagation()
this.introSwitch()
if (this.currentSlide === this.intro4) {
this.currentSlide.style.display = "none"
window.removeEventListener("keypress", this.introSwitch)
this.addKeyListeners()
}
})
}`
It takes quite a while to load the audio - the app proceeds like normal but with no audio - it usually can run as intended after a few minutes or a few refreshes, but I do want to get rid of that period. I tried to fix that using (document.readyState === "interactive") and if (allAudio.entries(audio => (audio.readyState === 4)))
It doesn't take the first key down event to change the intro slides, it takes the second - but I'm also not sure how to fix this. The slides are in an array that goes through each item and then makes them display as none. I also tried to add the event listener for the keyboard earlier, but to no avail.
I created an Audio visualizer using p5.js and React and i need some help.
I am trying to change the state of my button input text from Play to Stop when the song starts playing & from Stop back to Play when the user decides to stop the song at that moment.
I am using react-p5 library and everything works great until i setState when a user clicks the play button. Since that causes a re render it breaks my canvas.
Is there a way to only re-render the button element? I am not too sure how i would go about changing the inner text of my button without a state change?
import React, { useState } from 'react';
import Sketch from 'react-p5';
import 'p5/lib/addons/p5.sound';
import Beat from '../../instrumental.mp3';
const App = () => {
let width = 900;
let height = 600;
let song;
let fft;
const [flag, setFlag] = useState(true);
const preload = (p) => {
p.soundFormats('mp3');
song = p.loadSound(Beat);
}
const setup = (p, canvasParentRef) => {
// react-p5 conveniently initializes window.p5 thats why all the other p5's
// have been changed to p in order to create an FFT object
fft = new p5.FFT();
// console.log(p.point)
// use parent to render the canvas in this ref
// without it p5 will render the canvas outside of this component
const canvas = p.createCanvas(width, height).parent(canvasParentRef);
}
const playStop = () => {
if (song.isPlaying()) {
song.pause();
setFlag(true);
//p.noLoop();
} else {
song.play();
setFlag(false);
//p.loop();
}
}
const draw = p => {
p.background(0);
// by default the stroke color is black
// we need to change this in order to see the wave
p.stroke(255, 204, 0);
// no fill in between waves
p.noFill();
// returns an array with 1024 elements
let wave = fft.waveform();
p.beginShape();
// By looping through the waveform data, we are able
// to draw the waveform across the canvas
for (let i = 0; i < width; i++) {
// create an index that maps the for loop variable
// to the index of the wave we want
// value must be integer thats we we use floor
let index = p.floor(p.map(i, 0, width, 0, wave.length));
let x = i;
let y = wave[index] * 100 + height / 2;
p.vertex(x, y);
}
p.endShape();
}
return (
<div className='outerbox'>
<h1>Audio Visualizer</h1>
<Sketch preload={preload} setup={setup} draw={draw}/>
{flag ? <button onClick={playStop}>Play</button> : <button onClick={playStop}>Stop</button>}
</div>
);
}
export default App;
The sad thing is there aren't many resources available that includes react + p5.js
If anyone would like to take the time and clone this repository down in order to see what the problem might be, i would very much appreciate that.
Repo link: https://github.com/imperium11/audio-visualizer
npm i
npm run dev-build
npm start
The issue here is that each time you update state in a functional component the function gets called again. As a result each time state changes you re-declare your preload/setup/draw, because of the way react-p5 work, the running sketch will start using your updated draw function. However, the updated draw function expects fft to be defined, but the version of the fft variable referenced by the new draw function is undefined.
In order to fix this you can make any local variables that you sketch uses into state variables. In this example I've packed all the locals into one object:
const { useState } = React;
const Sketch = reactP5;
let v = 0;
const App = () => {
const width = 500;
const height = 300;
const [flag, setFlag] = useState(true);
const [locals, setLocals] = useState({});
const preload = (p) => {
p.soundFormats('mp3');
setLocals({
song: p.loadSound('https://www.paulwheeler.us/files/Ipu.wav')
});
}
const setup = (p, canvasParentRef) => {
// react-p5 conveniently initializes window.p5 thats why all the other p5's
// have been changed to p in order to create an FFT object
setLocals({
...locals,
fft: new p5.FFT()
});
// console.log(p.point)
// use parent to render the canvas in this ref
// without it p5 will render the canvas outside of this component
p.createCanvas(width, height).parent(canvasParentRef);
};
setup.version = v++;
const playStop = () => {
if (locals.song.isPlaying()) {
locals.song.pause();
setFlag(true);
//p.noLoop();
} else {
locals.song.play();
setFlag(false);
//p.loop();
}
}
const draw = p => {
p.background(0);
p.text(setup.version.toString(), 20, 20);
// by default the stroke color is black
// we need to change this in order to see the wave
p.stroke(255, 204, 0);
// no fill in between waves
p.noFill();
// returns an array with 1024 elements
let wave = locals.fft.waveform();
p.beginShape();
// By looping through the waveform data, we are able
// to draw the waveform across the canvas
for (let i = 0; i < width; i++) {
// create an index that maps the for loop variable
// to the index of the wave we want
// value must be integer thats we we use floor
let index = p.floor(p.map(i, 0, width, 0, wave.length));
let x = i;
let y = wave[index] * 100 + height / 2;
p.vertex(x, y);
}
p.endShape();
}
return (
<div className='outerbox'>
<span>Audio Visualizer</span>
{flag ? <button onClick={playStop}>Play</button> : <button onClick={playStop}>Stop</button>}
<Sketch preload={preload} setup={setup} draw={draw}/>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-p5#1.3.27/build/index.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script>
<div id="root"></div>
Another way to go would be to actually make the functions that use those local variables into state variables. That way they would only be declared the first time your component function is called for each instance of it. This may be a bit of a hack, but it has the advantage of working for rapidly updating local variables (if you were changing local variables in your draw() function, my understanding is that you would not want to make those into state variables, since high frequency state updates may impact performance.
const { useState } = React;
const Sketch = reactP5;
const App = () => {
const width = 500;
const height = 300;
let song;
let fft;
const [flag, setFlag] = useState(true);
const [sketchFns] = useState({
preload: (p) => {
p.soundFormats('mp3');
song = p.loadSound('https://www.paulwheeler.us/files/Ipu.wav');
},
setup: (p, canvasParentRef) => {
// react-p5 conveniently initializes window.p5 thats why all the other p5's
// have been changed to p in order to create an FFT object
fft = new p5.FFT();
// console.log(p.point)
// use parent to render the canvas in this ref
// without it p5 will render the canvas outside of this component
p.createCanvas(width, height).parent(canvasParentRef);
},
playStop: () => {
if (song.isPlaying()) {
song.pause();
setFlag(true);
//p.noLoop();
} else {
song.play();
setFlag(false);
//p.loop();
}
},
draw: p => {
p.background(0);
// by default the stroke color is black
// we need to change this in order to see the wave
p.stroke(255, 204, 0);
// no fill in between waves
p.noFill();
// returns an array with 1024 elements
let wave = fft.waveform();
p.beginShape();
// By looping through the waveform data, we are able
// to draw the waveform across the canvas
for (let i = 0; i < width; i++) {
// create an index that maps the for loop variable
// to the index of the wave we want
// value must be integer thats we we use floor
let index = p.floor(p.map(i, 0, width, 0, wave.length));
let x = i;
let y = wave[index] * 100 + height / 2;
p.vertex(x, y);
}
p.endShape();
}
});
return (
<div className='outerbox'>
<span>Audio Visualizer</span>
{flag ? <button onClick={sketchFns.playStop}>Play</button> : <button onClick={sketchFns.playStop}>Stop</button>}
<Sketch preload={sketchFns.preload} setup={sketchFns.setup} draw={sketchFns.draw}/>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-p5#1.3.27/build/index.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script>
<div id="root"></div>
I am working on a game app using React native and Matter.js.
I am trying to implement a system that adds points every time a bullet hits a target.
In order to do this, I am trying to use collisionStart to detect the collision.
However, even though the bullet and target collide only once, the event seems to be triggered 41 times.
This is the code:
Matter.Events.on(engine, 'collisionStart', (event) => {
let pairs = event.pairs
for (const pair of pairs) {
if (pair.bodyA.label === 'bullet' && pair.bodyB.label === 'Worm') {
console.log("target hit");
}
}
})
In the end, I'm planning to replace console.log with something that adds points. At the current moment, one collision seems like it would trigger the add points 41 times, which is obviously not ideal.
Any ideas what is happening here and how I can get this to trigger only once for one collision?
Try next example. I take it from my own project [you need little adaptd from ts to js]:
Matter.Events.on(this.starter.getEngine(), "collisionStart", function (event) {
root.collisionCheck(event, true);
});
public collisionCheck(event, ground: boolean) {
const myInstance = this;
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i !== j; ++i) {
const pair = pairs[i];
if (pair.activeContacts) {
if (pair.bodyA.label === "bullet" && pair.bodyB.label === "Worm") {
const collectitem = pair.bodyA;
this.playerDie(collectitem);
} else if (pair.bodyB.label === "bullet" && pair.bodyA.label === "Worm") {
const collectitem = pair.bodyB;
this.playerDie(collectitem);
}
// ....
}
}
}
public destroyBody = (destroyBody) => {
try {
Matter.Composite.remove(this.getWorld(), destroyBody);
} catch(err) {
console.log(err)
}
}
If you still have same problem , we can adapt also with flag PREVENT_DOUBLE_BY_1_SECOUND for example.
I am a new Developer and am working on a Tic Tac Toe SPA. I have a working game, but I want to customize the game tokens. I have tried creating a DOM element a few different ways, which were all successful. Here is the problem:
Every time I go to make a second move for a player, the DOM image disappears and reappears in the new square selected. Obviously this is not the desired action. Is there something I don't know about creating a DOM element. I have googled this and read countless articles and watched countless videos.
const stark = document.createElement('img')
stark.src = 'https://i.imgur.com/d70XlET.png'
stark.height = 80
stark.width = 80
const lanister = document.createElement('img')
lanister.src = 'https://i.imgur.com/d70XlET.png'
lanister.height = 80
lanister.width = 80
const play = (event) => {
if (gameOver === false) {
if (event.target.innerHTML === '') {
$('#' + event.target.id).append(turn)
}
}
}
'turn' is a variable that works with a toggle function to switch between players and stores whichever players turn it is(i.e. 'stark')
I would be really grateful if someone could point me in the direction of a resource where I could learn more about this.
const player1 = stark
const player2 = lanister
let turn = player1
let prevTurn = player2
const togglePrevTurn = () => {
if (!gameOver) {
if (prevTurn === player1) {
prevTurn = player2
} else {
prevTurn = player1
}
}
}
const toggleTurn = () => {
if (!gameOver) {
if (turn === player1) {
turn = player2
} else {
turn = player1
}
$('#message').text(turn + " 's turn")
}
}
Whenever you use Javascript's appendChild or jQuery's append, when you pass it an element, that element gets removed from its previous location in the DOM (if it's in the DOM), and then gets inserted at the new position. It sounds like what you need to do is explicitly create a new element each time, which you might do with cloneNode().
Also, probably best to name your variables precisely - if turn is an image, name it to make it clear that it's an image, perhaps currentPlayerImage.
In addition, because you already have a reference to the event.target, there's no need to reselect it with $('#' + event.target.id) - just select event.target:
const play = (event) => {
if (gameOver === false) {
if (event.target.innerHTML === '') {
$(event.target).append(currentPlayerImage.cloneNode());
}
}
}
I am new to react js. I am creating a comparison between user typing and actual sentence to be typed Somehow I am able to achieve this but It is not perfect like nested map is not rendering properly if letter typed correctly it should render green background My state is updated properly But my nested map Kinda not working there is a delay
Component Code
renderLine = () => {
let test = this.props.test.get('master')
return test.map(line => {
return line.check.map( (ltr,i) => ltr.status ? <span key={i} className="correct">{ltr.letter}</span> : ltr.letter )
})
};
handleKeyPress = e => {
if(e.charCode === 32) {
this.setState({
pushToNext:true,
currentTyping:""
})
}
};
handleInput = e => {
if(e.target.value !== " "){
let {storeValue} = this.state;
console.log(storeValue.length);
let updatedWord = e.target.value;
let updateArr = [];
if(storeValue.length === 0){
updateArr = storeValue.concat(updatedWord)
}else {
if(this.state.pushToNext){
updateArr = storeValue.concat(updatedWord)
}else {
storeValue.pop();
updateArr = storeValue.concat(updatedWord);
}
}
this.setState({
currentTyping:updatedWord,
storeValue:updateArr,
pushToNext:false
},() => {
let {storeValue} = this.state
let lastWordIndex = storeValue.length === 0 ? storeValue.length : storeValue.length - 1;
let lastLetterIndex = storeValue[lastWordIndex].length === 0 ? storeValue[lastWordIndex].length : storeValue[lastWordIndex].length - 1;
let lastWordValue = storeValue[lastWordIndex];
let lastLetterValue = lastWordValue[lastLetterIndex];
// console.log(lastWordIndex,lastLetterIndex,lastWordValue,lastLetterValue,"After tstae")
return this.props.compareCurrentTextWithMater(lastWordIndex,lastLetterIndex,lastWordValue,lastLetterValue)
});
}
};
Redux Reducer
import {FETCH_USER_TYPING_TEXT,COMPARE_TEXT_WITH_MASTER} from "../actions/types";
import {fromJS} from 'immutable';
const initialState = fromJS({
text:null,
master:[],
inputBoxStatus:false
});
export default function (state = initialState,action) {
switch (action.type){
case FETCH_USER_TYPING_TEXT:
return setTextManipulated(state,action);
case COMPARE_TEXT_WITH_MASTER:
return compareTextWithMaster(state,action)
default:
return state
}
}
const compareTextWithMaster = (state,action) => {
let {lastWordIndex,lastLetterIndex,lastLetterValue} = action;
let masterWord = state.get('master')[lastWordIndex];
let masterLetter = masterWord.check[lastLetterIndex];
let newState = state.get('master');
if(typeof masterLetter !== "undefined"){
if(masterLetter.letter === lastLetterValue){
masterWord.check[lastLetterIndex].status = true;
newState[lastWordIndex] = masterWord;
return state.set('master',newState)
}else {
masterWord.check[lastLetterIndex].status = false;
newState[lastWordIndex] = masterWord;
return state.set('master',newState)
}
}else {
console.log('Undefinedd Set Eroing or wrong Space Chratced set Box Red Colot',newState);
}
};
UPDATE
I did the same Logic with plain React.js it works Perfectly and nested map rendering the if else logic properly there is no on letter delay
https://codesandbox.io/s/zx3jkxk8o4
But the same logic with Redux State with immutable js Does'nt take effect with nested loop if else statement I don't know where the problem Relies ..and My Code Snippet will be little bit different from CodeSanbox COde But the Logic is Same
Probably, the diffing algorithm of react does see that oldState === newState and skips the re rendering. To avoid that situation, use a new object in the root of the state so that the above check returns false. I see that you use immutableJs, so maybe force re-render with componentShouldUpdate method instead.
Also consider using dev tools to step through the code line by line to see what is going on.
If nothing at all works, switch to something simpler with less dependencies and go from there, incrementally adding what you need.