React - sort array of child components with state - javascript

Currently I'm working on a react project, but I'm seeing some unexpected behavior when sorting an array of stateful child components.
If I have a parent component
export function Parent(){
const [children, setChildren] = useState([
{name:'Orange',value:2},
{name:'Apple',value:1},
{name:'Melon',value:3}
])
var count = 0
function handleSort() {
var newChildren=[...children]
newChildren.sort((a,b)=>{return a.value-b.value})
setChildren(newChildren)
}
return (
<div>
<button onClick={handleSort}>Sort</button>
{children.map((child) => {
count++
return(<ChildComp key={count} details={child}/>)
})}
</div>
)
}
And a child component
function ChildComp(props){
const[intCount,setIntCount] = useState(0)
function handleCount(){
setIntCount(intCount+1)
}
return (
<div>
<p>{props.details.name}</p>
<button onClick={handleCount}>{intCount}</button>
</div>
)
}
When the page first renders everything looks great, three divs render with a button showing the number of times it was clicked and the prop name as it was declared in the array. I've noticed that when I sort, it sorts the props being passed to the child components which then rerender, but the intCount state of the child component stays tied to the original location and is not sorted. is there any way to keep the state coupled with the array element through the sort while still maintaining state data at the child level, or is the only way to accomplish this to raise the state up to the parent component and pass a callback or dispatch to the child to update it?

The count is not is not sorted. It just got updated when you sorted.
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity
Every time you sort, key stay the same, as you use count.
Try using value as key
export function Parent(){
// ....
return (
<div>
<button onClick={handleSort}>Sort</button>
{children.map(child => {
return <ChildComp key={child.value} details={child}/> // key is important
})}
</div>
)
}
More info: https://reactjs.org/docs/lists-and-keys.html#keys

Related

Returning value from React child component

I am beginner in React so I apologize if this is a basic question. I am trying to build an online Sudoku grid using ReactJS but I am unable to render the digits in the field. I have made two components one being the Sudoku.js which renders the complete sudoku block and other being Node.js which consists of an input field wrapped inside a div. the functionality I want is to change the default value of the input field (which is "" in my case) whenever the user types a number. I have tried the below approach. It updates the value of my grid variable but does not show the updated value in the input field of Node. Kindly help.
Sudoku.js
const sudokugrid = (
<div>
{grid.map((row,rowIdx) => {
return (
<div key = {rowIdx} className="rowWrapper">
{row.map((col,colIdx) => {
var element = grid[rowIdx][colIdx].value;
const change = (event) => {
element = event.target.value;
grid[rowIdx][colIdx].value = element;
setGrid(grid);
return element;
}
return (
<Node
key = {colIdx}
onChangeValue = {change}
value = {element}
/>
)
})}
</div>
)
})}
</div>
);
here grid is a 2D array of 9x9 elements which have initial value of all the elements being "" , which are supposed to be updated when the user types in the value in the respective fields. the problem is that the value is updated when the user types the number but the number is not shown in the input field
The Node component is as follows:
function Node(props){
return (
<div className="box">
<input
className = "num"
type="number"
value = {props.value}
onChange = {props.onChangeValue}
/>
</div>
)
}
This is because react can't detect mutations. See this article. You have to store the sudoku grid in a state value and change the state with a callback function.
An example.
I suggest using an object map for storing the state because you can manage the values a lot easier.

How to immutable update an object given that we have to pass the same object as props?

I have an object amounts that gets updated on button clicks. Ans i pass that object as a prop to another component. What i am doing right now is updating object in mutable way on button click event.
onClick = e => {
amounts.map(
amount => (amount.tax = taxes ? 500 : 0)
);
}
<Display amounts={amounts} />
How can i update amounts in an immutable way?
As mentioned in the comments, there are a few things going on:
You are not updating the amounts Array reference, so React will not re-render based on this mutation.
You are using Array#map to update a single property. This will update the Object reference in the amounts collection.
There is no setAmounts or anything similar in order to update the value of the amount property in a parent component.
Assuming you are using useState in the <Display />s parent component, you will have to pass the setAmounts function to the <Display /> component using props.
<Display amounts={amounts} setAmounts={setAmounts} />
onClick = e => {
setAmounts(
amounts.map(
amount => ({ ...amount, tax: taxes ? 500 : 0 })
);
);
}

How to update redux store when calling a push method

I am using FieldArray , my requirement is add upto 3 elements and I am using push method for this. I have a redux variable/property to check whether elements have reached upto 3.But how I can achieve that store variable change when I do push an element(pushing an element into array should increase that property value.
{fields.map((doc, index) => (
<div>
<h4>Element #{index + 1}</h4>
<br/>
<div className="row">
{fields.length < 3 &&
<button type="button" className="btn btn-primary" onClick=
{() => fields.push({})}>
Add Element
</button>
</div>
</div>))}
Any pointers will be helpful, Thank you.
The only way to update redux store is to dispatch actions. Here, when you click on your button, you just execute a function that updates your current fields array.
Second point, when working on React and Redux, you need to modify your state immutably, without changing it, by passing a new object. For example :
state = {
cars: []
}
const newState = {
...state,
cars: ['Ferrari']
}

Get count of rows on the UI

I have a Parent component that makes an API call and passes the data to its children using a map function as the Parents API call returns an array. Now in the child component, each array result from the parent is used to make another API call. The second API call will populate the rows on the client. How do I get the count of the children rows that are shown on the clients' screen?
https://stackblitz.com/edit/rows-count has a working example, initially, the parent has an array of elements [1,2,3,4] and we are mapping each value in Parent and passing the current array value to the Child component.
In the Child component, we are checking if the passed value satisfies the value%2 === 0 condition, and based on it we are adding new values to the child's state. if the condition is satisfied we are creating a new array with the value as setHelper([value, value]) and if not satisfy the condition we are creating an array as setHelper([i*4,i*5]).
In the below example, the Parent has an array and mapping over the array to create 'n' number of children components and passing the array value as the prop to each Child. In the Child component, using useEffect and based on the value of the prop i using a state value helper and updating it to an array based on the condition.
if current value gives i%2 === 0 then the array value would be [i,i], else if we are adding [i*4,i*5] to the state and if i === 5 then we won't be displaying anything on the screen as now helper is not an array and is set to 0.
if helper is an array, we are mapping the array values inside the Child component to display.
How do I get the final count of the number of new rows in the Child component and read this value in the Parent component?
index.js
import React from 'react';
import Child from './Child'
const Parent = ()=>{
let parentState= [1,2,3,4,5]
return (
<>
<p>Parent Array is {parentState}</p>
<p>child new array is </p>
{parentState.map((i,index)=>(
<Child i={i}/>
))}
</>
)
}
export default Parent;
Child.js
import React ,{useEffect,useState}from 'react';
const Child = ({i})=>{
const [helper, setHelper]=useState(i)
useEffect(()=>{
if(i%2 === 0){
setHelper([i,i])
}else if(i == 5){
setHelper(0)
}
else{
setHelper([i*4,i*5])
}
},[])
if(Array.isArray(helper)){
return (
<>
{helper.map((i,index)=>(
<div className="child-row">
<p>-----</p>
<h4>{i}</h4>
<p>-----</p>
</div>
))}
</>
)
}
else{
return <></>
}
}
export default Child

Use of array.map and onchange in React

The code below contains an array.map function what is the function of term and i and where was it gotten from, and what does the array.map and the onchange do
import React, { Component } from 'react';
class Apps extends Component {
componentDidMount() {
}
iLikeFunctions() {
console.log('yay functions');
}
render() {
var array = ['here','we','go'];
var no = 'yes';
const display = 'My Name';
return (
<div>
<p>{display}</p>
<hr />
<input type="text" onChange={this.iLikeFunctions} />
<table>
<tbody>
{array.map((term,i) => {
no = 'no';
return (
<tr key={i}>
<td>{term}</td>
<td>{no}</td>
</tr>
)
})}
</tbody>
</table>
</div>
);
}
}
export default Apps;
Map:
The map() method creates a new array with the results of calling a provided function on every element in the calling array. So in the following line:
array.map((term,i)
You are mapping the array called array and looping through the array, assigning the word term for each value in the array and return a tr element for each array element with their respective value, index and variable string printed on the <tr>.
Key:
i is the index of the respective value which acts as a key since you didn't specify unique key ids for the elements.
A "key" is a special string attribute you need to include when creating lists of elements. Keys help React identify which items have changed, are added, or are removed.
Do note that it is not recommended to use indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state.
Check out the keys section in the official React Docs for a more in-depth explanation of keys.
onchange:
onchange watches the input field for any change and when it detects a change, it runs the iLikeFunctions().
tldr: The above code loops through array ['here','we','go']; and returns a <tr> for each value. It also runs the iLikeFunctions() whenever the input field value is changed.

Categories

Resources