title: Prevent rerender react array of objects
body: <p>I have an Ul component which contains an array of objects with different values in it. Each item called TestCase in this list has a button which makes a restcall and updates its object. However not all TestItems need to be updated. Only those whose button are clicked. The state of this array is stored in a parent continer component called TestCaseContainer. My button will updates the state accordingly to the effected TestItem in the array, however. This causes the whole list to rerender. How can I only have the changed TestItems rendered, instead of having the entire ul rendered every time an element is updated. I read about using useMemo so the component can memoize the passed down props, however I don't know how to implement this properly.</p>
  <p>How can I stop all the rerenders?</p>
  <p>Regression.js - Holds all the state</p>
  <pre><code>const Testing = forwardRef((props,ref) =&gt; {
  
  
    const templateTestItem = {id:0,formData:{date:'',env:'',assetClass:'',metric:'',nodeLevel:'',nodeName:'',testName:'',dataType:'',tradeId:''},results:[],isLoading:false}
    const testCaseRef = useRef()
    const [isRun, setIsRun] = useState(false)
    const [testItems, setTestItems] = useState([ templateTestItem])
    const [stats,setStats] = useState(null)
  
  
    const addTestItem = () =&gt; {
  
      const newIndex = testItems.length 
      // console.log(newIndex)
  
      const templateTestItem = {id:newIndex,formData:{date:'',env:'',assetClass:'',metric:'',nodeLevel:'',nodeName:'',testName:'',dataType:'',tradeId:''},results:[],isLoading:false}
      setTestItems([...testItems, templateTestItem])
  
    }
  
    const addUploadCases = (cases) =&gt; {
  
      setTestItems([])
      const UploadedItems = cases.map((item,index)=&gt;{
  
          return{
            id:index,
            formData:{
              date:item['date'],
              env:item['env'],
              assetClass:item['asset_class'],
              metric:item['metric'],
              nodeLevel:item['node_level'],
              nodeName:item['node_name'],
              testName:item['test_name'],
              dataType:item['dataType'],
              tradeId:item['tradeId']
            },
            results:[]
  
          }
  
      })
  
      setTestItems(UploadedItems)
  
    }
  
    const runAllTests = () =&gt; {
  
      testCaseRef.current.runAll()
    }
  
  
    const clearTestCases = () =&gt; {
  
      // console.log('Clear Test cases')
      setTestItems([])
  
      if (testItems.length == 0) {
        setTestItems([templateTestItem])
  
      }
  
    }
  
  
    const extractAllResults =()=&gt;{
      testCaseRef.current.extractAllResults()
    }
  
  
  
    const updateTestResults = useCallback( (result, index) =&gt; {
  
      console.log('Index:', index)
  
      setTestItems(prevObjs=&gt;(prevObjs.map((item)=&gt;{
        let updatedItem = { ...item, results: result }
        if(item.id==index) return updatedItem
        return item
      })))
  
    },[])
  
    return (
      &lt;div style={{ 'backgroundColor': '#1b2829', 'display': 'flex', }} className={styles.dashboard}&gt;
        &lt;Grid&gt;
          &lt;Row stretched style={{}} className={styles.buttonConsole}&gt;
  
              {&lt;ButtonConsole addTest={addTestItem} addUploadCases={addUploadCases} runAllTests={runAllTests} clearTestCases={clearTestCases} extractAllResults={extractAllResults}  /&gt;}
          &lt;/Row&gt;
  
          &lt;Row centered&gt;
            &lt;TestRunStats stats={stats}/&gt;
          &lt;/Row&gt;
  
          &lt;Row style={{ 'display': 'flex', 'flex-direction': 'column' }} &gt;&lt;TestCaseContainer countTestRunStats={countTestRunStats} updateTestResults={updateTestResults} isRun={isRun} ref={testCaseRef} testItems={testItems} /&gt; &lt;/Row&gt;
  {/* 
          &lt;Row&gt;&lt;/Row&gt;
          &lt;Row&gt;&lt;/Row&gt; */}
        &lt;/Grid&gt;
      &lt;/div&gt;
    );
  
  })
  </code></pre>
  <p>TestContainer.js</p>
  <pre><code>const TestCaseContainer = forwardRef((props, ref) =&gt; {
  
    const testCaseRef = useRef([])
  
    useImperativeHandle(ref, () =&gt; ({
  
  
      extractAllResults: async () =&gt; {
  
  
        const data = {
          data:[],
          summary:[]
        }
  
        testCaseRef.current.forEach(async (item, index) =&gt; {
  
          try {
  
            const workbook = item.extractAllResults()
            const summary = workbook['summary']
  
            workbook['data'].forEach(testData =&gt; {
              data['data'].push(testData)
  
            })
  
            data['summary'].push(summary)
  
  
  
          } catch (err) {
            console.log(err)
          }
  
  
        })
  
  
        await axios.post('http://localhost:9999/api/downloadresults', data).then(res =&gt; {
  
          console.log('res', res)
          const byteCharacters = atob(res.data);
          const byteNumbers = new Array(byteCharacters.length);
          for (let i = 0; i &lt; byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
          }
          const byteArray = new Uint8Array(byteNumbers);
          const blob = new Blob([byteArray], { type: 'application/vnd.ms-excel' });
          saveAs(blob, 'TestResults.xlsx')
  
  
        })
  
  
      },
  
      runAll: () =&gt; {
  
        testCaseRef.current.forEach(async (item, index) =&gt; {
          await item.runAll()
  
        })
      }
  
  
    }));
  
  
    const runTestCase = async (date, env, nodeLevel, nodeName, assetClass, metric, dataType, tradeId, testName, key) =&gt; {
  
  
  
      let testKey = key
  
      console.log('FEtCHING', testKey)
  
  
      try {
  
        const params = {
          nodeName,
          date,
          env,
          nodeLevel,
          assetClass,
          metric,
          dataType,
          tradeId,
          testName
        }
  
        const endpoint ={
          sensitivities:'sensitivities'
        }
  
  
        if (metric == 'DELTA_SENSITIVITIES') {
          const result = await axios.get('example.net/api/sensitivities', { params, }).then(response =&gt; {
  
            console.log('response.data', response.data)
            return response.data
  
          })
  
          if (result.data == 'none') {
  
            toast.error(`${date}-${metric}-${nodeName} failed queried! No valutations for trades`, {
              autoClose: 8000,
              position: toast.POSITION.TOP_RIGHT
            });
  
          } else if (result.data != 'none') {
  
            // setTestResult(result)
            props.updateTestResults(result, testKey)
            // updateTestResults(false,testKey,'isLoading')
  
  
            toast.success(`${date}-${metric}-${nodeName} Successfully queried!`, {
              autoClose: 8000,
              position: toast.POSITION.TOP_RIGHT
            });
  
          }
          // setTestResult(result.data)
        } else {
  
          await axios.get(`http://localhost:9999/api/metric/${metric}`, { params, }).then(response =&gt; {
  
  
            if (response.data != 'none') {
  
              props.updateTestResults(response.data, testKey)
  
              toast.success(`${date}-${metric}-${nodeName} Successfully queried!`, {
                autoClose: 8000,
                position: toast.POSITION.TOP_RIGHT
              });
  
  
            } else {
  
  
              toast.error(`${date}-${metric}-${nodeName} failed queried! No valutations for trades`, {
                autoClose: 8000,
                position: toast.POSITION.TOP_RIGHT
              });
  
  
            }
  
          })
  
        }
  
      } catch (error) {
  
        toast.error(`${date}-${metric}-${nodeName} failed queried! -${error
          }`, {
          autoClose: 8000,
          position: toast.POSITION.TOP_RIGHT
        });
  
      }
  
    }
  
  
    return (
      &lt;Segment style={{ 'display': 'flex', 'width': 'auto', 'height': '100vh' }} className={styles.testCaseContainer}&gt;
        &lt;div style={{ 'display': 'flex', }}&gt;
        &lt;/div&gt;
        &lt;ul style={{overflowY:'auto',height:'100%'}} className='testItemContainer'&gt;
          {
            
            // memoTestTwo
  
            // testList
            props.testItems.map((item, index) =&gt; {
  
  
              let testName
              if (item['formData']['testName'] == '') {
                testName = `testRun-${index}`
              } else {
                testName = item['formData']['testName']
              }
  
              return &lt;TestCase testResult={item['results']} runTestCase={runTestCase} isRun={props.isRun} ref={el =&gt; (testCaseRef.current[index] = el)} testKey={index} key={index} date={item['formData']['date']} env={item['formData']['env']} assetClass={item['formData']['assetClass']} metric={item['formData']['metric']} nodeLevel={item['formData']['nodeLevel']} nodeName={item['formData']['nodeName']} testName={testName} dataType={item['formData']['dataType']} tradeId={item['formData']['tradeId']} hierarchy={hierarchy} /&gt;
            })
          }
  
        &lt;/ul&gt;
      &lt;/Segment&gt;
    )
  
  
  })
  </code></pre>
  <p>TestCase.js - the individual item rendered from mapping!</p>
  <pre><code>const TestCase = forwardRef((props, ref) =&gt; {
  
      const [isLoading, setIsLoading] = useState(false)
      const inputRefs = useRef()
      const outputRefs = useRef()
  
      useImperativeHandle(ref, () =&gt; ({
  
        extractAllResults: () =&gt; {
          return outputRefs.current.extractAllResults();
        },
  
  
        runAll: () =&gt; {
          inputRefs.current.runAll()
        },
  
      }));
  
  
  
      const runSingleTestCase = async (date, env, nodeLevel, nodeName, assetClass, metric, dataType, tradeId, testName, key) =&gt; {
  
        setIsLoading(true)
        await props.runTestCase(date, env, nodeLevel, nodeName, assetClass, metric, dataType, tradeId, testName, key)
        setIsLoading(false)
      }
  
  
  
      const convertDate = (date) =&gt; {
  
        if (date) {
          const newDate = date.split('/')[2] + '-' + date.split('/')[0] + '-' + date.split('/')[1]
          return newDate
  
        } else {
          return date
        }
  
      }
  
  
  
      return (
        &lt;Segment color='green' style={{ 'display': 'flex', 'flexDirection': 'column', }}&gt;
          &lt;div style={{ 'display': 'flex', 'justify-content': 'space-between' }}&gt;
  
            &lt;div style={{ 'display': 'flex', 'height': '30px' }}&gt;
              &lt;Button
                // onClick={props.deleteSingleTest(props.testKey)}
  
                icon=&quot;close&quot;
                inverted
                size=&quot;tiny&quot;
                color='red'
              &gt;&lt;/Button&gt;
  
            &lt;/div&gt;
  
            &lt;RegressionInput runSingleTestCase={runSingleTestCase} isRun={props.isRun} testKey={props.testKey} ref={inputRefs} nodeNames={props.hierarchy} runTestCase={props.runTestCase} date={convertDate(props.date)} testName={props.testName} env={props.env} assetClass={props.assetClass} metric={props.metric} nodeLevel={props.nodeLevel} nodeName={props.nodeName} dataType={props.dataType} tradeId={props.tradeId} /&gt;
  
            &lt;TestCheck pass={props.testResult ? props.testResult['CHECK'] : null} /&gt;
  
  
          &lt;/div&gt;
  
          {
            isLoading ? (&lt;Loading type={'circle'} style={{ 'display': 'flex', 'flexDirecton': 'column', 'justify-content': 'center', 'align-items': 'center', 'marginTop': '50' }} inline /&gt;) : (
              &lt;RegressionOutput ref={outputRefs} testName={props.testName} testResult={props.testResult} /&gt;
            )
          }
  
        &lt;/Segment&gt;
  
      )
  
  })
  </code></pre>