React re-rendering every cell of the table - javascript

I have a really big rerender problem in every cell of the table because of the handleClick function even used useCallback.
My cell component like; (wrapped with React.memo())
<StyledTableCell onContextMenu={() => handleClick()} style={{ backgroundColor: `${(flash && data) ? 'yellow' : color}`, transition: `${flash ? 'none' : '1s'}` }}>
<div className='cell-size' >
{data}
</div>
</StyledTableCell >
export const MemoizedDataCell = React.memo(DataCell)
and this cell goes to the upper level like ;
<MemoizedDataCell handleClick={() => onCellClick(selectedOrders, i, Boolean(val))} data={val} key={colName + i} color={cellColor(i, z)} />
And finally, in the top parent, I use this function for the child ;
const onCellClick = React.useCallback((myOrder: any, i: number, isFilled: boolean) => {
setCanDeal(isFilled)
let side = i < 3 ? 'bids' : 'asks'
console.log(myOrder)
if (i < 0) {
return null
}
if (!myOrder.asks && !myOrder.bids) {
const detail = {
instrName: myOrder.instName, maturity: myOrder.maturity, side: side.charAt(0).toUpperCase() + side.slice(1, -1), contract: `${myOrder.instName} ${myOrder.maturity}`,
price: '0', quantity: '0', totalQuantity: '0', tradableQuantity: '0'
}
return setOrderDetail(detail)
}
let orderID = myOrder[`${side}`][0]?.OrderID
let instrName = myOrder[`${side}`][0]?.InstName
let maturity = myOrder[`${side}`][0]?.FirstSequenceItemName
let contract = `${instrName} ${maturity}`
let broker = myOrder[`${side}`][0]?.Broker
let price = myOrder[`${side}`][0]?.Price
let quantity = myOrder[`${side}`][0]?.Volume
let timeInForce = myOrder[`${side}`][0]?.OrderType.replace(/([A-Z])/g, ' $1').trim()
let lastUpdTime = new Date(myOrder[`${side}`][0]?.DateTime).toLocaleString()
let tradableQ = '-'
let totalQ = '-'
let priceStat = myOrder[`${side}`][0]?.Status
side = side.charAt(0).toUpperCase() + side.slice(1, -1)
const detail = {
orderID: orderID, instrName: instrName, maturity: maturity, side: side, contract: contract,
price: price, priceStatus: priceStat,
quantity: quantity, totalQuantity: totalQ,
tradableQuantity: tradableQ, broker: broker, lastUpdate: lastUpdTime, timeInForce: timeInForce
}
setOrderDetail(detail)
}, [])
Any help ll be appreciated. Thank you.

How are you memoizing your Cell ?
If you use memo or useMemo, it does a === comparison with each previous props
And as you're doing:
<MemoizedDataCell handleClick={() => onCellClick(selectedOrders, i, Boolean(val))} .. />
You are creating a new function at each render for handleClick, so MemoizedDataCell will be re-rendered.
So you need to memoize this function
const _handleClick = useCallback(() => onCellClick(...), []) // With good values in dependency array
<MemoizedDataCell handleClick ={_handleClick} />
This way, you're passing as prop the same function reference at each render
Also note that doing something like:
<Component style={{ backgroundColor: ... }} />
Will also re-renders every time as you are creating a new Object each time

Related

reverse method is called every time i type in my input box (onChange)

i have a variable that i'm using to store the reversed array returned from an api, but everytime i type in the input box to create a new activity, the array reverses. My guess is that it's something with the state and the onChange function?
(i want the array to be reversed so the most recent post is shown at the top and i don't have to scroll past tons of posts to see if mine was created, by default it's the newest post shown last)
this is the component so far:
`
const Activities = ({ token, activities, fetchActivities }) => {
const [createName, setName] = useState('');
const [createDesc, setDesc] = useState('');
const reverseActivities = activities.reverse();
async function addActivity() {
try {
const newActivity = {
token: token,
name: createName,
description: createDesc
}
await createNewActivity(token, newActivity);
fetchActivities();
alert('success!')
}
catch (err) {
console.error('addActivity-activities.js FAILED:', err);
}
}
return (
<div>
<h1>Activities</h1>
<form>
<h2>Create a new activity</h2>
<input
type='text'
placeholder='Name'
onChange={(e) => {
e.preventDefault();
setName(e.target.value)
}} />
<input
type='text'
placeholder='Describe activity'
onChange={(e) => {
e.preventDefault();
setDesc(e.target.value)
}} />
<button
type='submit'
onClick={(e) => {
e.preventDefault();
addActivity();
}}
>Create Activity</button>
</form>
{
reverseActivities.map((activity) => {
const { name, id, description } = activity
return (
<div key={id}>
<h3>{name}</h3>
<p>description: {description}</p>
</div>
)
})
}
</div>
)
}
`
i've tried to move the variable declaration to the top of the component as well as doing activties.reverse().map(....) instead of declaring the variable and then mapping over the array. Neither really made a difference.
Every time state changes, react will re-render your component. Because you update state each time a character changes activities.reverse(); will keep getting called. This could be solved with a useMemo but in your case I do not think that would solve the problem.
I think your confusion stems from the fact that reverse modifies the array it is called on and returns a reference to the original array.
> const array = [1,2,3,4,5]
undefined
> const reversed = array.reverse()
undefined
> reversed
[ 5, 4, 3, 2, 1 ]
> array
[ 5, 4, 3, 2, 1 ]
> reversed == array
true
You could create a new array with [...array] then reverse that one.
> const array = [1,2,3,4,5]
undefined
> const reversed = [...array].reverse()
undefined
> reversed
[ 5, 4, 3, 2, 1 ]
> array
[ 1, 2, 3, 4, 5 ]

List items with inputs - how to add list object to an array when input has value?

I have created a list of objects and each list item has input within:
{flowers_data?.map((flower) => {
return (
<>
<div className={classes.Nested_Flower_Container} key={flower.id}>
<div className={classes.Nested_Flower_Name}>
{flower.name}
</div>
<div className={classes.Nested_Flower_Input} style={{ marginRight: '0.2em' }}>
<TextField
id="Amount"
label="Amount"
variant="outlined"
size="small"
type="number"
onChange={(e) => {
setAmount(e.target.value);
handleAddList(e.target.value, flower);
}}
className={classes_2.root}
/>
</div>
</div>
</>)
})}
How can I add an object that has a value in input to an array? I tried to do this using a function that I created, but each time I change one element's target.value and move on to the next item to change its input value, there is only one element in the array with the latest target.value. And after modifying the inputs, when I try to output the values outside that function with e.g. a button, the add_flowers_tab array is empty.
handleAddList function:
let temp_flower: Flower;
let add_flowers_tab: Flower[] = [];
const handleAddList = (targetValue: string, flower: Flower) => {
temp_flower = {
"id": flower.id,
"name": flower.name,
"price": flower.price,
"amount": Number(targetValue),
"creation_date": flower.creation_date
}
if (targetValue === '') {
/* Delete flower when input is empty */
add_flowers_tab.forEach(tabFlower => {
if (tabFlower.id === temp_flower.id) {
const indexOfDelete = add_flowers_tab.indexOf(tabFlower);
add_flowers_tab.splice(indexOfDelete, 1);
}
})
}
if (targetValue !== '') {
/* Add flower to tab when input has value */
if (add_flowers_tab.length > 0) {
/* When input changes, delete flower with old input value and add the new one */
add_flowers_tab.forEach(tabFlower => {
if (tabFlower.id === temp_flower.id) {
const indexOfDelete = add_flowers_tab.indexOf(tabFlower);
add_flowers_tab.splice(indexOfDelete, 1);
add_flowers_tab.push(temp_flower);
}
})
}
else {
/* Add new flower as a first element in the array */
add_flowers_tab.push(temp_flower);
}
/*
Displays an array with only the most recently added temp_flower, even though
several inputs in list have values
*/
console.log(add_flowers_tab);
}
}
Here is the minimum solution for generating a list on Inputs that each update a single element in a list on state.
export const ManyInputs = ({inputs}) => {
const [list, setList] = useState(inputs);
const setIndividual = (value, id) =>
// Update a single field in the object in the list that matches the given id
setList(list.map(l => l.id === id ? ({...l, value}) : l));
return list.map(i =>
// Send the id back to help match the object
<TextField onChange={(e) => setIndividual(e.target.value, i.id)} />
);
};
You can have one React state, which has an array of objects that you enter through your "mapped" component.
const [tmpAmount, setTmpAmount] = React.useState<{flower:Flower,amount:number}[]>([])
// fill the temporary state with generated flowers only once
React.useEffect(()=>{
setTmpAmount(flowers_data.map((flwr) =>{flower:flwr, amount:0}))
},[])
and replace the onChange()
onChange={(e) => {
// find existing flower to edit the amount of it
let indexToChange = flowers_data.findIndex((element) => element.id === flower.id)
// update and replace the array variable with updated object
setAmount2((prev) =>
[
...prev.slice(0, indexToChange),
{
...prev[indexToChange],
amount: e.target.value,
},
...prev.slice(indexToChange + 1),
]);
}
You can also check if the array changes and print it:
React.useEffect(()=>console.log(tmpAmount),[tmpAmount])

React 16.13.1 - child component does not re-render when props change

I have a parent component and a child component. The child component initially renders data into a form but upon changing the data, the child component does not update.
Parent Component:
import React from 'react'
import { connect } from 'react-redux'
import styles from '../styles'
import ExpressionsForm from './expressionsForm'
class EditCondition extends React.Component {
constructor (props) {
super(props)
this.state = {
condition: null
}
this.updateExpression = this.updateExpression.bind(this)
this.changes = false
}
componentWillMount () {
let conditionid = this.props.data.id
let condition = this.props.conditions.find(c => {
return (c.id = conditionid)
})
this.setState({ condition })
}
updateExpression (e) {
let expressionid = e.currentTarget.dataset.expressionid
let field = e.currentTarget.dataset.field
let value = e.target.value
let condition = this.state.condition
let expression = condition.expressions[expressionid]
expression[field] = value
condition.expressions[expressionid] = expression
this.changes = true
this.setState({ condition })
console.log('updateExpression condition: ', condition)
}
render () {
let condition = this.state.condition
if (!this.state.condition) {
return (
<div>
The selected condition with ID "{this.props.data.id}" did not load. It
may not exist. Refresh and try again.
</div>
)
}
let groupOptions = this.props.gambitGroups.map(g => {
return (
<option value={g.id} key={'group' + g.id}>
{g.name}
</option>
)
})
console.log('RENDER editCondition: ', condition) // <-- Note: This always logs as expected
let expressionsJSX = condition.expressions.map((expression, i) => {
expression.id = i
console.log('expression: ', expression) // <-- Note: This always logs as expected
return (
<ExpressionsForm
key={'expressionsForm_' + i}
expression={expression}
deleteExpression={this.deleteExpression}
updateExpression={this.updateExpression}
updateExpressionData={this.updateExpressionData}
/>
)
})
return (
<table>
<thead>
<tr>
<th {...styles.modal.tableHeaderLeftAlign}>
Device & Data Point
</th>
<th {...styles.modal.tableHeaderLeftAlign}>Operator</th>
<th {...styles.modal.tableHeaderLeftAlign}>Value</th>
<th {...styles.modal.tableHeaderLeftAlign}>PlateValue</th>
<th {...styles.modal.tableHeaderLeftAlign}> </th>
</tr>
</thead>
<tbody>{expressionsJSX}</tbody>
</table>
)
}
}
export default connect(
(state, ownProps) => ({
user: state.user,
users: state.users,
gambitGroups: state.gambitGroups,
// deviceGroups: state.deviceGroups,
conditions: state.conditions,
reactions: state.reactions,
setEditMode: ownProps.setEditMode,
navByName: ownProps.navByName
}),
dispatch => ({
addImage: file => dispatch({ type: 'UPDATE_CONDITION_LOGO', file }),
updateCondition: condition =>
dispatch({ type: 'UPDATE_CONDITION', condition })
})
)(EditCondition)
And Child Component:
import React from 'react'
import { connect } from 'react-redux'
import styles from '../styles'
class ExpressionsForm extends React.Component {
constructor (props) {
super(props)
this.state = {}
this.updateExpression = this.updateExpression.bind(this)
}
updateExpression (e) {
this.props.updateExpression(e)
}
render () {
let expression = this.props.expression
console.log('expression: ', expression) // Note: logs initial render only.
let data = expression.data
let deviceId = data.deviceId
let dataPointIndex = data.dataPointIndex
let operator = expression.operator
let plateValue = expression.plateValue
let value = expression.value
console.log('RENDER expressionForm: ', expression) // Note: logs initial render only
let deviceOptions = this.props.devices.map((device, i) => {
return (
<option value={device.id} key={'device_' + i}>
{device.userAssignedName}
</option>
)
})
let dataPointOptions = this.props.devices[0].inputs.map((input, i) => {
return (
<option value={input.id} key={'input_' + i}>
{input.name} currentValue: {input.value}
</option>
)
})
let operatorOptions = ['==', '!=', '<=', '>=', '<', '>'].map(
(operator, i) => {
return (
<option value={operator} key={'operator_' + i}>
{operator}
</option>
)
}
)
return (
<tr>
<td>
<select
{...styles.modal.inputSexy}
style={{ marginBottom: '20px' }}
data-field='deviceid'
data-expressionid={expression.id}
value={deviceId}
onChange={this.updateExpressionData}
>
<option value=''></option>
{deviceOptions}
</select>
<select
{...styles.modal.inputSexy}
data-field='dataPointIndex'
data-expressionid={expression.id}
value={dataPointIndex}
onChange={this.updateExpressionData}
>
<option value=''></option>
{dataPointOptions}
</select>
</td>
<td>
<select
{...styles.modal.inputSexy}
style={{ width: '75px' }}
data-field='operator'
data-expressionid={expression.id}
value={operator}
onChange={this.updateExpression}
>
<option value=''></option>
{operatorOptions}
</select>
</td>
<td>
<input
{...styles.modal.inputSexy}
style={{ width: '50px' }}
data-field='value'
data-expressionid={expression.id}
value={value}
onChange={this.updateExpression}
/>
</td>
<td>
<input
{...styles.modal.inputSexy}
style={{ width: '88px' }}
data-expressionid={expression.id}
data-field='plateValue'
value={plateValue}
onChange={this.updateExpression}
/>
</td>
<td>
<i className='fa fa-close'
data-expressionid={expression.id}
onClick={this.deleteExpression}
></i>
</td>
</tr>
)
}
}
export default connect(
(state, ownProps) => ({
user: state.user,
users: state.users,
devices: state.devices,
gambitGroups: state.gambitGroups,
// deviceGroups: state.deviceGroups,
conditions: state.conditions,
reactions: state.reactions,
setEditMode: ownProps.setEditMode,
navByName: ownProps.navByName
}),
dispatch => ({
addImage: file => dispatch({ type: 'UPDATE_XXX', file })
})
)(ExpressionsForm)
I have an array of objects in the redux store called Conditions. The parent component gets an ID of one of these conditions, finds the correct condition, and loads it into state via componentWillMount to be modified by the user. The condition has an array of objects on it called expressions. Each of these expressions are passed to the child component called ExpressionsForm.
so we loop over the expressions via the map function and return the resulting JSX as expressionsJSX.
let expressionsJSX = condition.expressions.map((expression, i) => {
expression.id = i
console.log('expression: ', expression) // <-- Note: This always logs as expected
return (
<ExpressionsForm
key={'expressionsForm_' + i}
expression={expression}
deleteExpression={this.deleteExpression}
updateExpression={this.updateExpression}
updateExpressionData={this.updateExpressionData}
/>
)
})
Note that the has the expression passed to it expression={expression}
And in the child component's render you see
let expression = this.props.expression
console.log('expression: ', expression) // Note: logs initial render only.
Since this is a prop, whether it's being console.log'd or rendered into some JSX doesn't matter - when the prop changes the changes should also be re-rendered. BUT it's not doing it in this case. Why?
For example, I have 1 expression saved on 1 condition. It renders, I click into the plateValue input field of the expression - which contains a 5 by default - and attempt to add a 6 after the 5. When the parent component updates state an re-renders I see the in the console.log's that the expression's plateValue field now contains a '56'...it just doesn't render in the child component....!?
Here is an example console.log
Initial Render:
RENDER editCondition: {id: "1", group: 1, name: "Temperature >= 75F",
meta: "If >= 75F in greenhouse turn on AC until 5 degrees cooler than
75F", expressions: Array(1)} editCondition.jsx:191 expression: {data:
{…}, operator: ">=", value: "75", plateValue: "5", id: 0}
expressionsForm.jsx:39 RENDER expressionForm: {data: {…}, operator:
">=", value: "75", plateValue: "5", id: 0}
Click into plateValue field and add a '6', parent rerenders...and:
editCondition.jsx:188 RENDER editCondition: {id: "1", group: 1, name:
"Temperature >= 75F", meta: "If >= 75F in greenhouse turn on AC until
5 degrees cooler than 75F", expressions: Array(1)}
editCondition.jsx:191 expression: {data: {…}, operator: ">=", value:
"75", plateValue: "56", id: 0} editCondition.jsx:153 STATE SET!
updateExpression condition: {id: "1", group: 1, name: "Temperature >=
75F", meta: "If >= 75F in greenhouse turn on AC until 5 degrees cooler
than 75F", expressions: Array(1)}
I see a 'plateValue: "56"' in there. So why isn't it rerendering in
the child component? So confused.
I've tried componentWillReceiveProps, componentWillUpdate, et al. I can't even get these to fire off a console.log.
Something is going on that I can't figure out. I've been doing React for a long time and I'm stumped. That doesn't happen very often anymore.
Thanks in advance for your help
PS I did look at getDerivedStateFromProps - It's great that the documentation provides examples, but they don't explain what the props and state parameters actually are. The docs suck. Their explanation sucks. Their example doesn't illustrate what it actually does. I only ever use componentWillReceiveProps to know when a prop has changed, and then update state or whatever. getDerivedStateFromProps just confuses me. None the less I played around with it and couldn't get it to work either.
It looks like the same expression object is being passed in all the time.
React checks the props that a component receives for changes when deciding to render. It finds that none of the props items have changed, they are all the same objects as before, and concludes that the child component does not need to be rerendered. It will not do a deep inspection of all properties of each prop.
This also explains why a rerender can be forced by making a copy of the expression object. The copy is always a new object, thus causing a rerender, regardless if any of its content have changed or not.
You could avoid this situation as you do already, by making a copy, or by dissecting the expression object into its properties and then feeding each of those as separate props into the child.
As a final note, a copy can also be made by passing it in as expression={{...expression}}.

How to find specific items in an array in React/Framer?

I am pulling down results from an API, like so:
const [state, setState] = React.useState({
matches: undefined,
chosenBets: [{}]
});
const API = "https://api.myjson.com/bins/i461t"
const fetchData = async (endpoint, callback) => {
const response = await fetch(endpoint);
const json = await response.json();
setState({ matches: json });
};
And rendering JSX based off it using the map() function:
export function MatchCardGroup(props) {
return (
<div>
{props.matches.map((match, i) => {
return (
<MatchCard
key={i}
matchCardIndex={i}
team_home={match.teams[0]}
team_away={match.teams[1]}
league_name={match.sport_nice}
odd_home={match.sites[0].odds.h2h[0]}
odd_draw={match.sites[0].odds.h2h[1]}
odd_away={match.sites[0].odds.h2h[2]}
onClick={props.onClick}
timestamp={match.timestamp}
/>
);
})}
</div>
);
}
I then have a card which has odds on it, each odd with its own click event:
export function MatchCard(props) {
const [state, setState] = React.useState({
selection: {
id: undefined
}
});
const {
timestamp,
team_home,
team_away,
league_name,
odd_away,
odd_draw,
odd_home,
onClick,
matchCardIndex,
selection
} = props;
const odds = [
{
id: 0,
label: 1,
odd: odd_home || 1.6
},
{
id: 1,
label: "X",
odd: odd_draw || 1.9
},
{
id: 2,
label: 2,
odd: odd_away || 2.6
}
];
const handleOnClick = (odd, oddIndex) => {
// need to changhe the selection to prop
if (state.selection.id === oddIndex) {
setState({
selection: {
id: undefined
}
});
onClick({}, matchCardIndex);
} else {
setState({
selection: {
...odd,
team_home,
team_away
}
});
onClick({ ...odd, oddIndex, team_home, team_away, matchCardIndex });
}
};
React.useEffect(() => {}, [state, props]);
return (
<div style={{ width: "100%", height: 140, backgroundColor: colour.white }}>
<div>
<span
style={{
...type.smallBold,
color: colour.betpawaGreen
}}
>
{timestamp}
</span>
<h2 style={{ ...type.medium, ...typography }}>{team_home}</h2>
<h2 style={{ ...type.medium, ...typography }}>{team_away}</h2>
<span
style={{
...type.small,
color: colour.silver,
...typography
}}
>
{league_name}
</span>
</div>
<div style={{ display: "flex" }}>
{odds.map((odd, oddIndex) => {
return (
<OddButton
key={oddIndex}
oddBackgroundColor={getBackgroundColour(
state.selection.id,
oddIndex,
colour.lime,
colour.betpawaGreen
)}
labelBackgroundColor={getBackgroundColour(
state.selection.id,
oddIndex,
colour.lightLime,
colour.darkBetpawaGreen
)}
width={"calc(33.3% - 8px)"}
label={`${odd.label}`}
odd={`${odd.odd}`}
onClick={() => handleOnClick(odd, oddIndex)}
/>
);
})}
</div>
</div>
);
}
In my App Component I am logging the returned object from the click event:
const onClick = obj => {
// check if obj exists in state.chosenBets
// if it exists, remove from array
// if it does not exist, add it to the array
if (state.chosenBets.filter(value => value == obj).length > 0) {
console.log("5 found.");
} else {
console.log(state.chosenBets, "state.chosenBets");
}
};
And what I want to do is this:
When the user clicks an odd of any given match, add that odd to chosenBets
If the user deselects the odd, remove that odd from chosenBets
Only 1 odd from each of the 3 possible odds of any match can be selected at any time
Bonus points: the selected odd is selected based on the global state from App, instead of local state. This is so if I edit the array elsewhere, it should update in the UI.
Any help would be greatly appreciated, I'm lost here!
Link to Codesandbox
I've taken a short look at your project, and here are a few pointers to help you out:
Objects are only equal by reference.
This means that
{ id: 0, matchCardIndex: 8 } === { id: 0, matchCardIndex: 8 }
is false, even if you expect it to be true. To compare them, you need to compare every key in the object:
value.id === obj.id && value.matchCardIndex === obj.matchCardIndex
This also affects the filter call you have in the index.tsx, so you should change the comparison there to something similar to
state.chosenBets.filter(value => value.id === obj.id && value.matchCardIndex === obj.matchCardIndex)
State should only live in one place
As you already mentioned, it would be better to keep the state in your index.tsx if it also you needed there, and don't keep it locally in the components further down the tree. I'd suggest having the components only render the state, and have handlers to change the state.
Example
Here's a fork of your code sandbox I think implements it in a way that you described: https://codesandbox.io/s/gifted-star-wg629-so-pg5gx

how to make a dynamic text show the correct sentence in react when deleting a component (custom html element)

In React, upon deleting a component, I want to make a dynamic sentence shows the correct sentence like this in app.js:
let awesomePhrase = '';
if (!this.state.showPersons) {
awesomePhrase = 'Nobody is here, it seems :/';
}
if (this.state.showPersons && this.state.persons.length === 2) {
awesomePhrase = "All aboard :D";
}
if (!this.state.persons.filter(p => p.id === 1)) {
awesomePhrase = "Where's Matin?!";
}
if (!this.state.persons.filter(p => p.id === 2)) {
awesomePhrase = "Where's Mobin?!";
}
It doesn't show any of the sentence when I delete id 1 or id 2.That is, neither "where's Matin?!" nor "Where's Mobin?!".
But the two first sentences work fine.
(EDIT: every piece of code below is within app.js, the main file)
For deleting:
deleteHandler = index => {
const persons = [...this.state.persons].filter(
person => person.id !== index
);
this.setState({ persons });
};
The State:
state = {
persons: [
{ id: 1, name: 'Matin', age: 27 },
{ id: 2, name: 'Mobin', age: 26 }
],
showPersons: false,
...
};
The component within the render of the class:
{this.state.persons.map(person => {
return (
<Person
key={person.id}
name={person.name}
age={person.age}
click={() => this.deleteHandler(person.id)}
/>
);
})}
the part of render where dynamic text is used:
return (
<div>
...
<h2>{awesomePhrase}</h2>
...
</div>
)
The problem with your code is the filter function. Filter will return an empty array if no elements passed the test, and in Javascript, an empty array is not a falsy value.
The condition !this.state.persons.filter(p => p.id === 2) will always be false.
The proper function to use in this situation is Array.some, which return a boolean value depends on the result of the test function.
Be aware of the return type and the falsiness / truthiness in Javascript.
I think I found a workaround
instead of filter I used find, and tried to check them within deleteHandler method. Also added an independent awesomePhrase to State.
So:
deleteHandler = index => {
const persons = [...this.state.persons].filter(
person => person.id !== index
);
if (persons.length === 0) {
this.setState({ awesomePhrase: 'where did they all gone?' });
}
if (persons.find(p => p.name === 'Matin')) {
this.setState({ awesomePhrase: 'Where is Mobin?' });
}
if (persons.find(p => p.name === 'Mobin')) {
this.setState({ awesomePhrase: 'Where is Matin?' });
}
this.setState({ persons });
};
state = {
persons: [
{ id: 1, name: 'Matin', age: 27 },
{ id: 2, name: 'Mobin', age: 26 }
],
...,
showPersons: false,
mobin: true,
awesomePhrase: ''
};
return (
<div className="App">
...
<h2>{this.state.awesomePhrase || awesomePhrase}</h2>
...
</div>
);
I'd welcome any suggestion to help improve my code further or correct it properly. I just made it work now.

Categories

Resources