reactjs setstate not working in function that has .map

Tags: , , ,



Good Morning.

Need help, i have the below script, the thing is the setState is not working, I think I’m missing something here? or Am i doing wrong. the “return” in the below is inside “.map” so i could display the 3 file in the array. Thanks

  constructor(props) {
super(props);
this.state = {

  // fileName: ['SAMPLE DOCUMENT 1', 'SAMPLE DOCUMENT 2', 'SAMPLE DOCUMENT 3'],
  file: [
    {fileName: 'SAMPLE DOCUMENT 1', id: 123456, hash: '9153BB2EFC17BF53218FA15C977EDCD3', fileStatus: 
'PENDING APPROVAL', fileId: 'APFEX-9153BB2E-C6F90E', isViewed: 'false', activateButton: false},
    {fileName: 'SAMPLE DOCUMENT 2', id: 124234, hash: '9153BB2EFC17BF53218JEFSFH77EDCD3', fileStatus: 
'PENDING APPROVAL', fileId: 'APFEX-9153BB2E-C6KS0E', isViewed: 'false', activateButton: false},
    {fileName: 'SAMPLE DOCUMENT 3', id: 134564, hash: '9153BBMSJFOWN562DW689FWS641WES63', fileStatus: 
'PENDING APPROVAL', fileId: 'APFEX-9153BB2E-CSS9HG', isViewed: 'false', activateButton: false}
  ],     
  
  };
  }

 activatebutton = (key) => {
  const {activateButton} = key
  this.setState({ activateButton: true }, () => {
  }); 

 }

return ( 

 {this.state.file.map(file => (
  <TableRow className={this.state.classes.row} key={file.id} data-id={file.id}>   
    <CustomTableCell align="left" className={classes.row}>
      <a
        id={`download-${file.id}`}
        // onClick={() => downloadFile({ transactionId, fileId, id, fileName })}
        onClick={() => this.activatebutton(file)}
        rel="noreferrer"
        style={{textDecoration:'none',color:'#126ebd',cursor:'pointer'}}
        // eslint-disable-next-line no-script-url
        href="javascript:void(0);"
        
      >
        {file.fileName}
      </a>
    </CustomTableCell>
    <TableRow/>
 )

Answer

I imagine you want to recreate the file array, just changing the activateButton property of the clicked item to true. To do this in React, you must clone the array, mapping all items to themselves, except the one that you clicked. This one you create from scratch, spreading all the old properties and setting activateButton to true. Here’s the code:

activatebutton = (file) => (event) => {
  event.preventDefault(); // prevents the link from reloading the page
  const { id } = file;

  this.setState((state) => {
    const list = state.file;
    const newList = list.map((item) =>
      item.id === id ? { ...item, activateButton: true } : item
    );

    return { file: newList };
  });
};

Comments:

activatebutton = (file) => (event) => {

We want activatebutton to be a second order function, which receives the clicked item and then the event. Even better would be to pass only the id directly.

this.setState((state) => { … })

Whenever you want to update the state based on itself, use the functional setState. Pass a function whose parameter is the current state and the return is an update object. In this case, the file property of state.

item.id === id ? { …item, activateButton: true } : item

This is short for:

if (item.id === id) {
  return Object.assign({}, item, {activateButton: true})
} else {
  return item
}

I made a CodeSandbox Demo which even includes as the doubleClick event the toggling of the item.



Source: stackoverflow