每次按下键时,React 都会重新渲染所有内容

时间:2021-05-17 04:58:13

标签: reactjs react-functional-component react-memo

我在下面有一个图表和一个输入字段。基本上用户可以提出一个问题,图表会相应地改变。这是它的样子

enter image description here

这是下面的代码示例(忽略 CoreUI 语法)

<CRow>
   <CCol xs="12" sm="12" lg="12">
      <CCard id="answerScreen" style={{height: "500px"}}>
      {
      !tempResponse.loading?  tempResponse.data.map(value => (  
      <ChartRender
         key={uuidv4()}
         data={value} 
         />
      ))
      :  
      <Loader/>
      }
      </CCard>
   </CCol>
</CRow>

<CRow>
   <CCol xs="12" sm="12" lg="12">
      <CCard>
        
            <CCardBody>
               <CForm className="form-horizontal">
                  <CFormGroup>
                           <CInputGroup size="lg" className="input-prepend">
                              <CInputGroupPrepend>
                                 <CInputGroupText className="">@Ask</CInputGroupText>
                              </CInputGroupPrepend>
                              <CInput 
                              size="16" 
                              type="text" 
                              value={userInput || ""}
                              onChange={e=> handleTextBoxInput(e)}
                              onClick={e=> handleTextBoxClick(e)}   
                              onKeyPress={e => handleTextBoxEnter(e)}
                              id="userQuery"
                              />
                            
                              <CInputGroupAppend>
                                 <CButton color="primary">Send</CButton>
                              </CInputGroupAppend>
                           </CInputGroup>
                  </CFormGroup>
               </CForm>
            </CCardBody>
        
      </CCard>
   </CCol>
</CRow>

这就是我定义状态的方式

const [userInput, setUserInput] = React.useState("");
const [tempResponse, setTempResponse] = React.useState({
        data: []
})

怀疑这部分代码有问题

<CInput 
   size="16" 
   type="text" 
   value={userInput || ""}
   onChange={e=> handleTextBoxInput(e)}
   onClick={e=> handleTextBoxClick(e)}   
   onKeyPress={e => handleTextBoxEnter(e)}
   id="userQuery"
/>

我什至尝试像这样将 useCallback 添加到 onChange 函数

const handleTextBoxInput =  useCallback(e =>{   
        e.preventDefault();
        setUserInput(e.target.value)
}, [])

但是没有帮助。我什至阅读了 memo,但不确定在我的情况下在哪里或如何应用它。我做错了什么?

观察

@Matthew 所述,箭头语法每次都会创建不同的回调,这有助于重新渲染,因此必须删除。

但即使在删除它之后,每次按下一个键时图表都会重新呈现。我正在使用 Chartjs,它在内部使用 canvasChartjs 有问题吗?

2 个答案:

答案 0 :(得分:2)

您对有问题的代码是正确的。

<CInput 
   size="16" 
   type="text" 
   value={userInput || ""}
   onChange={e=> handleTextBoxInput(e)} // performance issue
   onClick={e=> handleTextBoxClick(e)}  // performance issue 
   onKeyPress={e => handleTextBoxEnter(e)} // performance issue
   id="userQuery"
/>

当上面的代码多次运行时,每次都会重新创建函数。因此,与其这样做,以下内容就足够了

<CInput 
   size="16" 
   type="text" 
   value={userInput || ""}
   onChange={handleTextBoxInput}
   onClick={handleTextBoxClick}
   onKeyPress={handleTextBoxEnter}
   id="userQuery"
/>

useCallback 钩子返回一个回调函数。您可以简单地将返回值用作普通事件回调。

答案 1 :(得分:1)

在您的输入中,每次按键都会触发两个事件 - onKeyPressonChange - 删除 onKeyPress

我怀疑 handleTextBoxEnter 调用了更新 tempResponse 的 setTempResponse。设置 UI 所依赖的状态将触发重新渲染。

您还应该使用 onSubmit 事件处理表单提交。如果某个元素在表单内具有焦点并且按下了 Enter 按钮 - 它将触发 onSubmit。

<form onSubmit={handleTextBoxEnter}></form>

此外,如果一个键发生变化,React 将重新渲染。您正在密钥中调用一个函数,因此它会在每次更新时更新。

tempResponse.data.map((value, i) => (  
 <ChartRender key={`chart-${i}`} data={value} />
))

仅供参考,手动创建 UUID 以防止过度使用密钥。

export const YourComponent = (): JSX.Element => {
  const [userInput, setUserInput] = useState('');
  const [tempResponse, setTempResponse] = useState({ data: [], loading: true });

  useEffect(()=>{
    // handle initial data loading and set loading to false
  }, [])

  const handleSubmit = (e) => {
    e.preventDefault();
    setTempResponse(your_state_data);
  };

  // e.preventDefault shouldn't be used here and is not required
  const handleChange = ({ target }) => setUserInput(target.value);

  if (tempResponse.loading) {
    return <Loading />;
  }

  // action is set to # for iOS - an action is required to show the virtual submit button on the keyboard
  return (
    <>
      <form action="#" onSubmit={handleSubmit}>
        <input defaultValue={userInput} onChange={handleChange} type="text" />
        <button type="submit">Submit</button>
      </form>
      {!!tempResponse.length &&
        tempResponse.data.map((value, i) => (
          <ChartRender key={`chart-${i}`} data={value} />
        ))}
    </>
  );
};