Not able to change state properties of redux reducer in function call - reactjs

I have this redux reducer case, which has multiple conditions to work with. I am getting issue in changing value of state properties.
//code explaination:
In the second condition I am checking if action.hoverIndex > action.dragIndex, and if the condition is true then it creates "td" variable to hold the new state. After state change I have to make some changes in the properties of the state.
In the else part of the 2nd condition when I change the properties of "td" by assigning new value in for loop, it works as expected, but same thing when I try in "nestedConditionHandler" it does not work. not sure what is causing this issue.
const cmdArray = [
"ECommand",
"BCommand",
"CCommand",
"DCommand",
];
const cmdPair = {
ECommand: "EndE",
BCommand: "EndB",
CCommand: "EndC",
DCommand: "EndD",
};
const notFirstCommand = [
"PCommand",
"QCommand",
"XCommand"
];
case actionTypes.UPDATE_COMMAND_WORKFLOW:
// Deep clone of current state
const temp = cloneDeep(state);
// Draged command state
const dragCard = temp[action.dragIndex];
const tempDragCard = cloneDeep(dragCard);
// hovered command state
const hoverCard = temp[action.hoverIndex];
const tempHoverCard = cloneDeep(hoverCard);
//condition one
if (
action.hoverIndex === 0 &&
notFirstCommand.includes(dragCard.class_name)
) {
return temp;
}
//condition 2
// command drag from top to bottom
if (action.hoverIndex > action.dragIndex) {
const td = update(temp, {
$splice: [
[action.dragIndex, 1],
[action.hoverIndex, 0, tempDragCard],
],
});
let startIndex;
let endconditionIndex;
// for nested commands
const nestedConditionHandler = (i, currState) => {
startIndex = i + 1;
endconditionIndex = td.indexOf(
td.find((cur) => cmdPair[currState.class_name] === cur.class_name)
);
while (startIndex < endconditionIndex) {
const tempTd = td[startIndex];
console.log(tempTd);
console.log(currState.indent + 1);
td[startIndex].indent = currState.indent +1
td[startIndex].parentId = currState.parentId
if (cmdArray.includes(td[startIndex].class_name)) {
nestedConditionHandler(startIndex, td[startIndex]);
}
startIndex += 1;
}
};
if (
dragCard.class_name === "ECommand" ||
dragCard.class_name === "EfCommand"
) {
for (let i = action.dragIndex; i < action.hoverIndex; i++) {
td[i].indent = dragCard.indent + 1;
td[i].parentId = dragCard.parentId;
}
} else {
for (let i = action.dragIndex; i < action.hoverIndex; i++) {
td[i].indent = dragCard.indent;
td[i].parentId = dragCard.parentId;
if (cmdArray.includes(td[i].class_name)) {
nestedConditionHandler(i, td[i]);
}
}
}
return td;
}
// command drag from bottom to top , next condition

Related

Single row value change is reflected in all the drawers

I have two drawer components and inside those drawer user can select maximum 3 rows. Each row has a Accessory Qty property which cannot be greater than Max Qty.
Two Drawers, each with clickHandler which triggers inner row sections
Each row has Accessory Qty and Max Qty. as mentioned above.
Selecting all the rows and clicking Associate to all which adds the selected rows in the other drawers. You can see in the breadcrumbs that currently I'm in SET F. Now after going back to Image one, when I click the 2nd drawer which is SET H I can see that the selected Items are added here.
Now if I change any value in any of the rows of SET H the same change is reflected in the SET F
This is the whole problem, I want the changes not to reflect in other drawers.
For this data is getting fetched from the API. I'm setting a copy of the data in the backupdata. Then with the index and values I'm changing the values in the corresponding rows.
handleQtyChange
const handleQtyChange = (event, i) => {
console.log("Set Index QTY", index, i);
const backupdata = [...billOfMaterials];
var data = event.target.value;
if (backupdata[index].accessory_details[i].accessory[0].code !== "") {
var accesory_code =
backupdata[index].accessory_details[i].accessory[0].code;
var max_qty = backupdata[index].accessory_details[i].max_qty;
var updated_qty = event.target.value;
if (parseFloat(updated_qty) > parseFloat(max_qty)) {
setMessageDialog({
isOpen: true,
message: "Qty must be less than or equal to Max qty",
severity: "error",
});
} else {
if (data == "" || (!isNaN(+data) && parseFloat(data) >= 0)) {
console.log("AccessoryChildIndex", i);
console.log("Max qty", accessoryListWithMaxQty);
console.log("Set Index", index);
setMessageDialog({
isOpen: false,
message: "",
severity: "error",
});
var accessory_total_qty_index = backupdata[
index
].accessory_total_qty.findIndex(
(x) =>
x.code == backupdata[index].accessory_details[i].accessory[0].code
);
console.log("accessory_total_qty Index", accessory_total_qty_index);
backupdata[index].accessory_total_qty[accessory_total_qty_index].qty =
updated_qty;
backupdata[index].accessory_details[i].accessory_qty = updated_qty;
backupdata[index].accessory_details[i].total = (
parseFloat(
event.target.value == undefined || event.target.value == ""
? 0
: event.target.value
) * parseFloat(backupdata[index].accessory_details[i].rate)
).toFixed(2);
backupdata[index].grand_total = grandTotal();
setBillOfMaterials(backupdata);
if (!checkBomAccessoryWiseTotalQty(accesory_code, max_qty)) {
setMessageDialog({
isOpen: true,
message: "Qty limit exceed max qty would be " + max_qty,
severity: "error",
});
backupdata[index].accessory_total_qty[
accessory_total_qty_index
].qty = 0;
backupdata[index].accessory_details[i].accessory_qty = 0;
backupdata[index].accessory_details[i].total = 0;
backupdata[index].grand_total = grandTotal();
setBillOfMaterials(backupdata);
}
console.log("Change BOM", billOfMaterials);
}
}
}
};
handleAssociateToAll
const handleAssociateToAll = () => {
//var data = [...billOfMaterials];
var backupdata = [...billOfMaterials];
//var backupdata = JSON.parse(JSON.stringify([...billOfMaterials]));
var selectedData = backupdata[index].accessory_details.filter((el) => {
return el.is_selected == true;
});
console.log(selectedData);
console.log(index);
backupdata.map((el, i) => {
if (index != i) {
console.log(selectedData);
selectedData.map((dt) => {
dt.accessory_qty = "0";
dt.is_selected = false;
let checkAccessory = backupdata[i].accessory_details.find((data) => {
return (
data.accessory[0].code != "" &&
data.accessory[0].code == dt.accessory[0].code
);
});
console.log("Check", checkAccessory);
if (checkAccessory == undefined) {
backupdata[i].accessory_details = [
dt,
...backupdata[i].accessory_details,
];
}
});
backupdata[index].accessory_total_qty &&
backupdata[index].accessory_total_qty.map((qty) => {
qty.qty = "0";
let checkTotalQty = backupdata[i].accessory_total_qty.find(
(data) => {
return data.code == qty.code;
}
);
if (checkTotalQty == undefined) {
backupdata[i].accessory_total_qty = [
...backupdata[i].accessory_total_qty,
qty,
];
}
});
}
});
setBillOfMaterials(backupdata);
checkAllSelected();
};
console.log("bill", billOfMaterials);
I guess I've explained well. So how do I keep the other drawer's data unchanged and change only the selected drawers rows.
The api response
After clicking Associate to all:
The overall bill, the selected rows are added in accessory_total_qty:

Why my array getting appended instead of clearing and Adding new data

I am trying to achieve a method in which the array steps got filled with new data every time I click on the button of Create New array, but instead of that, the data is getting appended instead of updating.
here are my states :
const [arr , setArray] = useState(createArray())
const [steps , setSteps] = useState([]);
const [selectedAlgorithm , setSelectedAlgorithm] = useState ();
here is my create new Array function :
const handleCreateNewData = ()=>{
let newarr = createArray();
setArray ([]);
setArray([...newarr]);
setSteps ([]);
setTimeout(()=>{
if ( algorithms[selectedAlgorithm] !== undefined){
algorithms[selectedAlgorithm](arr, steps , setSteps);
console.log('running')
}
},2000)
}
here is my bubble sort algorithm :
export const BubbleSort = (array , steps ,setSteps) =>{
let funarray = new Array();
funarray = [...array] ;
for (let i = 0 ; i < funarray.length-1 ; i++){
for(let j = 0 ; j < funarray.length-1 ; j++){
if(funarray[j]>funarray[j+1]){
[funarray[j],funarray[j+1]] = [funarray[j+1],funarray[j]]
setSteps([...steps, funarray])
steps.push(funarray.slice());
console.log('Working')
}
}
}
return funarray;
}
What is supposed to do is every time I click on create new array it should generate a new set of arrays but instead of creating new arrays it just appending the new arrays in the old steps.
You can create a temp array to hold the steps, then when the loops are done, call setSteps:
const BubbleSort = (array, steps, setSteps) => {
let funarray = [];
funarray = [...array];
let temp = [];
for (let i = 0; i < funarray.length - 1; i++) {
for (let j = 0; j < funarray.length - 1; j++) {
if (funarray[j] > funarray[j + 1]) {
[funarray[j], funarray[j + 1]] = [funarray[j + 1], funarray[j]];
temp.push(funarray)
}
}
}
setSteps(temp);
return funarray;
};
Sample: https://codesandbox.io/s/cool-wind-ijj7z?file=/src/App.js

PointCloud Component render issue fetch() custom data [react-three-fiber]

Based on a snippet of original r3f-example found in PointCloud.js
Tested by myself, this above original component is able to render pointcloud by pushing individual x y z value into the for-loop in Particle() function.
I modified it and added a `fetch()' method to retrieve a custom data txt file, snippet as shown below,
...
export function Particles() {
const [positions, colors] = useMemo(() => {
let positions = [], colors = []
positions.length = 3
colors.length = 3
const HEADER_SIZE = 4;
let stream, longArray, len;
let clusterCount ;
let xy_size ;
let clusterSize = [];
let XY_arr = [];
fetch(map)
.then((r) => r.text())
.then(text => {
stream = text.toString().split("\n"); // split by next line
longArray = stream.slice(2,); // remove header from main longArray
len = longArray.length;
for (let i = 0, count = 0; i < len; i += HEADER_SIZE ) {
xy_size = longArray.slice((i + HEADER_SIZE - 1), (i + HEADER_SIZE));
XY_arr.push(longArray.slice((i + HEADER_SIZE ), (i + HEADER_SIZE + xy_size*2)));
console.log(" Points in PointCloud " + count + ": " + xy_size );
clusterSize.push(xy_size);
clusterCount = count;
i += xy_size*2;
count ++;
}
for (let i = 0; i < (clusterCount-2); i++) {
for (let j = 0; j < clusterSize[i]*2; j+=2) {
positions.push( XY_arr[i][j] )
positions.push(0)
positions.push( XY_arr[i][j+1] )
colors.push(1)
colors.push(0.5)
colors.push(0.5)
console.log( XY_arr[i][j] );
}
}
}
)
return [new Float32Array(positions), new Float32Array(colors)]
}, [])
...
...
, map is the custom text file in string, with single data line-by-line
The fetch() method is able to read a custom pointcloud file into XY_arr as an object of Array(). I have checked that XY_arr[i][j] in the nested-forloop are able to return correct x and z value in console.
Current problem is that no pointcloud being rendered onto <Canvas />
Is the problem caused by position.push() nested loop being inside of 'fetch()' method ? And how to resolve. Thank you.
better use const [state, set] = useState() and then fetch in useEffect calling "set" when you're done. putting an async fetch request inside useMemo is practically a side-effect in the render function - which isn't good, nor will it work like that.

How do I structure my React Component? (Too bloated)

I'm making a card game and I have a parent component which holds all my methods. The methods are my "logic" and it determines how to set the randomly generated hand according to 'house-way'. Think of house-way as a set of rules on how to play a certain hand.
I'm having a problem with how to structure my components. I've heard that you should keep your components small and my component is already 300 lines long.
Any advice on how to restructure this? I've tried putting methods on a different file and importing them, but I had trouble when it comes to setting state. In other words, 'this.setState()' throws an error unless it is a method of a class.
the code is a mess, but I basically need help with how to make my component less bloated.
export default class Layout extends Component {
constructor(props) {
console.log("starting up");
super(props);
//each hand holds a randomly generated tile object from { tilesSet }
this.state = {
//needs empty spots for when (mounting) <Hands hand1={this.state.hand[0].img} /> else error since hand[0] doesnt exist.
hand: ["", "", "", ""],
cards: false,
pairName: '',
rule: '',
show: false,
history: [],
test: 'test'
};
//binding in the constructor is recommended for performance.
this.handleToggle = this.handleToggle.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleHW = this.handleHW.bind(this);
this.assignHands = this.assignHands.bind(this);
this.checkPair = this.checkPair.bind(this);
this.checkTeenDey = this.checkTeenDey.bind(this);
this.hiLowMiddle = this.hiLowMiddle.bind(this);
this.compare = this.compare.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
//n = pairTL, n2 = otherTL
split(n, n2){
//Gee Joon
if (n[0].pair === 1) {
let combo1 = baccaratCount(n2[0], n[0]);
let combo2 = baccaratCount(n2[1], n[1]);
//if it meets the split requirements...
if((combo1 >= 7 && combo2 >= 9) || (combo1 >= 9 && combo2 >= 7)){
console.log('got em', combo1, combo2);
return true;
}
else {
return true;
}
//Teen/Dey
// } else if(high[0].val === 2) {
// var combo1 = baccaratCount(high[0].val, low[0].val);
// var combo2 = baccaratCount(high[0].val, low[1].val);
// //checks if any of the tiles are 7,8, or 9. for 9 gong and wong.
// var check7_9 = low[0].val >= 7 && low[0].val <= 9;
// var check7_9_2 = low[1].val >= 7 && low[1].val <= 9;
// //regular 6-8 split rule.
// if((combo1 >= 6 && combo2 >= 8) || (combo1 >= 8 && combo2 >= 6)){
// moveTiles("split");
// return true;
// //we might have 7,8,9 with T/D. (with 8 and 9, it turns to 0 and 1, so we need this part.)
// } else if (check7_9 === true || check7_9_2 === true){
// //if both are 7,8, or 9
// if (check7_9 === check7_9_2){
// moveTiles("split");
// return true;
// //only if 1..
// } else if (check7_9 === true && check7_9_2 === false){
// if (low[1].val >= 3 && low[1].val <=6){
// moveTiles("split");
// return true;
// } else {
// moveTiles();
// return true;
// }
// //if other one...
// } else{
// if (low[0].val >= 3 && low[0].val <=6){
// moveTiles("split");
// return true;
// } else {
// moveTiles();
// return true;
// }
// }
// //does not split.
// } else {
// moveTiles();
// return;
// }
// } else {
// // all other pairs. split pairs are in one array with a length of 2. ex: [7, 9]
// var combo1 = baccaratCount(high[0].val, low[0].val);
// var combo2 = baccaratCount(high[0].val, low[1].val);
// if(combo1 >= high[0].split[0] && combo2 >= high[0].split[0]){
// moveTiles("split");
// } else {
// moveTiles();
// }
// return true;
// }
}
}
//checks for pairs. takes an array as arg
checkPair(hand){
for(let i = 0; i < hand.length; i++) {
for (let ii = 0; ii < hand.length; ii++) {
// if there is a pair and it is not comparing to itself.
if (hand[i].pair === hand[ii].pair && i !== ii) {
let pairTL = hand.filter((x) => x.rank === hand[i].rank); //array of the pair tiles
let otherTL = hand.filter((x) => x.rank !== hand[i].rank); // array of the other 2 tiles. use these two to move tiles accordingly
//if we split this pair...
if (hand[i].split !== false) {
//returns true if it split..
if(this.split(pairTL, otherTL)) {
let copyArr = [pairTL[0], otherTL[0], pairTL[1], otherTL[1]];
this.setState(() => ({hand: copyArr}));
}
else {
let copyArr = otherTL.concat(pairTL);
this.setState(() => ({hand: copyArr}));
return true;
}
}
//don't split
else {
let copyArr = otherTL.concat(pairTL); //concats the two small arrays together and renders.
this.setState(() => ({hand: copyArr, pairName: pairTL[0].name, rule: 'Don\'t Split'}))
return true;
}
}
}
}
return false; // no pairs
}
//will not execute if there is a pair...(checkPair returns true)
checkTeenDey(hand){
//true if we have teen or dey
let teenDey = hand.find((el) => el.val === 2) !== undefined;
//if true...
if(teenDey){
let tile = hand.find((el) => el.val === 2); // teen/ dey object
let tempArr = hand.filter((el) => el.name !== tile.name); //new arr without marked teen/dey. arr.length = 3
let secondTeenDey = tempArr.find((el) => el.val === 2); //second teen/dey (not pair)
let seven = tempArr.find((el) => el.val === 7);
let eight = tempArr.find((el) => el.val === 8);
let nine = tempArr.find((el) => el.val === 9);
//if there is a second teen/dey
if (secondTeenDey){
let twoArr = tempArr.filter((el) => el.name !== secondTeenDey.name);
console.log(tile, secondTeenDey, twoArr);
return true;
}
//look for 7,8,9
else if (seven){
console.log (seven);
return true;
}
else if(eight){
return true;
}
else if(nine){
return true;
}
}
// no teen or dey...
else{
return false;
}
}
//point system used for sort()
compare(a,b){
let comparison = 0;//no change
if(a.realValue < b.realValue){
comparison = -1;//a comes before b
}
else if(a.realValue > b.realValue){
comparison = 1;//b comes before a
}
return comparison;
}
//will not execute if there is a teen dey...
hiLowMiddle(hand){
//makes a new copy of hand and sorts it using sort()'s point system.
let sortedArr = hand.slice().sort(this.compare); //slice used, else mutates hand.
let tempHair = [sortedArr[0], sortedArr[3]];
let tempBack = [sortedArr[1], sortedArr[2]];
let hiLow = tempHair.concat(tempBack); //newly sorted arr
this.setState(() => ({hand: hiLow, rule: 'Hi-Low-Middle'}));
}
//generates new hand and updates them to state.
assignHands() {
let tempArr = [0, 0, 0, 0]; //filler array
let testArr = tilesSet.slice(); //filler array. tilesSet is untouched
//loops through and assigns random tile from deck
let newArr = tempArr.map((x) => {
let counter = Math.floor(Math.random()* (testArr.length - 1));
//used to hold the selected obj. needed since splice changes arr.length and we splice/display the different indexes.
let dummyArr = testArr[counter];
testArr.splice(counter, 1);
return dummyArr;
})
//updates state
this.setState({hand: [newArr[0], newArr[1], newArr[2], newArr[3]], show: true, history: [...this.state.history, [...newArr]]})
}
handleSubmit = (e) => {
e.preventDefault();
}
//toggle effect.
handleToggle = () => {
this.setState(() => ({cards: !this.state.cards}));
}
handleClick = () => {
assignHands(tilesSet);
//works, but not 100% properly. the changes are one step behind. fix async.
//check to see the history length. max set # 20
if(this.state.history.length >= 10){
let temp = this.state.history.slice();
temp.shift();
this.setState(() => ({history: temp}))
}
}
//House Way
handleHW(){
if(!this.checkPair(this.state.hand)){
if(!this.checkTeenDey(this.state.hand)){
this.hiLowMiddle(this.state.hand);
}
}
}
render() {
return (
<div>
{this.state.show ? <h1>{baccaratCount(this.state.hand[0], this.state.hand[1]) + '/' + baccaratCount(this.state.hand[2], this.state.hand[3])}</h1> : <h1>Press New Hand to Start</h1>}
<form onSubmit={this.handleSubmit}>
<label>
<input type='text'/>
</label>
<button>submit</button>
</form>
<Input test={this.state.test}/>
<Hands
cards={this.state.cards}
hand1={this.state.hand[0].img}
hand2={this.state.hand[1].img}
hand3={this.state.hand[2].img}
hand4={this.state.hand[3].img}
/>
<Buttons
type="button"
className="btn btn-dark"
handleClick={this.handleClick}
handleHW={this.handleHW}
hand={this.state.hand}
/>
<h2>Value of tiles: {this.state.hand[0].val ? this.state.hand[0].val : "0"} - {this.state.hand[1].val ? this.state.hand[1].val : "0"} - {this.state.hand[2].val ? this.state.hand[2].val : "0"} - {this.state.hand[3].val ? this.state.hand[3].val : "0"}</h2>
<h2>Pair Name: {this.state.pairName}</h2>
<h2>Rule: {this.state.rule}</h2>
<h2>
History: <div>{this.state.history.map((el) => <li key={Math.random()}>{el.map((ele) => ele.name+ '--')}</li>)}</div>
</h2>
</div>
);
}
}

React state array object changes without setState

I have an array which is a state of the React component. This array is a checklist.
var units1 = this.state.units;
when I update units1, this.state.units changes without the this.setState({ units: units1 })
I use this.setState({ a: 2 }); just to see if the array was updated without this.setState({ units: units2 });
this.state.units gets its value from props so if the state changes the props also changes.
handleItemChange(e) {
var units1 = this.state.units.slice();
var unit_name = parseInt(e.target.attributes.getNamedItem('data-unit_name').value);
var new_unit;
if (!e.target.checked && this.state.units && this.state.units.length > 0) {
this.state.units.map((unit) => {
if (unit_name == unit.codpacunidad) {
if (unit.topics && unit.topics.length > 0) {
unit.topics.map((topic) => {
if (topic.codpacunidadtema == e.target.name) {
new_unit = unit;
var index = units1.indexOf(unit);
//units1.splice(index, 1);
units1 = update(units1, {$splice: [[index, 1]]})
var topics1 = unit.topics.slice();
index = topics1.indexOf(topic);
//topics1.splice(index, 1);
topics1 = update(topics1, {$splice: [[index, 1]]})
new_unit.topics = topics1;
}
});
}
}
});
} else {
var found_unit = false;
var name = parseInt(e.target.name);
var order = parseInt(e.target.attributes.getNamedItem('data-order').value);
var unit_order = parseInt(e.target.attributes.getNamedItem('data-unit_order').value);
if (this.state.units && this.state.units.length > 0) {
this.state.units.map((unit) => {
if (unit.codpacunidad == unit_name) {
found_unit = true;
new_unit = unit;
var index = units1.indexOf(unit);
units1.splice(index, 1);
var new_topic = {
codpacunidadtema: name,
orden: order
};
var topics2 = new_unit.topics;
new_unit.topics = update(topics2, { $push: [new_topic]});
}
});
}
if (found_unit == false) {
new_unit = {
codpacunidad: unit_name,
orden: unit_order,
topics: [{codpacunidadtema: name, orden: order }]
};
}
}
// var units2 = update(units1, { $push: [new_unit]});
// this.setState({ units: units2.sort(function(a, b) {
// return parseInt(a.orden) - parseInt(b.orden);
// })
// });
this.setState({ a: 2 }); //just to test if the array gets updated without this.setStaet({ units: units2 })
}
Anybody knows why this is happening?
As #Alexander van Oostenrijk said to make deep copy of array.
Because array are passed by reference which mean memory address of the array is passed not the value of array.
var units1 = this.state.units.slice();
Now units1 has the reference address of that array any change made to units1 or this.state.units.slice() will change value of both.As basically they are using address and if one change the value at address then both will read changed value.Hope you understand
To create deep copy you can create new object like
var units1 = Object.assign([],this.state.units)
This will create new object with data of this.state.units
extras I think you do not need .slice().

Resources