首页 > 解决方案 > 为什么当我更改状态时我的 React 应用程序没有重新渲染?

问题描述

我有一个使用以下模型的 React 应用程序:

export interface DataModel {
    itemId:         string,
    itemName:       string,
}

我为模型定义一个初始值,如下所示:

export const INIT_ITEM:DataModel = {
    itemId:         "",
    itemName:       "",
}

这在 Dashbaord 组件中使用,该组件显示了一个允许用户设置名称、最小值和最大值的表单。姓名为必填项;minimumValue 和 maximumValue 不是,但如果它们都存在,则 maximumValue 需要大于 minimumValue。此验证在提交表单时完成。这是仪表板组件:

interface ErrorHolder { // Holds error strings
  selectedItems: string,
  selection1:    string,
  selection2:    string,
  valid:         boolean
}

const INIT_VALIDATION:ErrorHolder = {
  selectedItems: "",
  selection1:    "",
  selection2:    "",
  valid:         false
}

// availableItems is an array of <DataModel>, prepopulated with available options
// selectedItems is an array of <DataModel>, and is initially empty
// selection1 and selection2 are both <DataModel> objects, which are initialised as above
//
// The items from availableItems are displayed with checkboxes. The user must select at least one item to //  display on the Dashboard; the list of selected items is stored as selectedItems
// The user must also select an item to be 'selection1' and a different item to be 'selection2'
//
const Dashboard = ( {availableItems, selectedItems, setSelectedItems, selection1, setSelection1, selection2, setSelection2} ) => { 
                                          
  const [valid, setValid] = useState<ErrorHolder>(INIT_VALIDATION);  // Initialise error strings

  const handleChangeItemSelected = (event:React.ChangeEvent<HTMLInputElement>, checked:boolean) => {

    const itemId:number = event.target.value;

    if(checked) {
      // Checkbox is checked, so this item should be shown
      const newSelectedItems = [...selectedItems];
      const fullItem = availableItems.filter(item => item.itemId === itemId);
      newSelectedItems .push(fullItem[0]);
      setSelectedItems(newSelectedItems );
    }else {
      // Checkbox is not checked, so this item should not be shown
      const newSelectedItems = selectedItems.filter(item=> item.itemId !== itemId);
      setSelectedItems(newSelectedItems );
    }
  }

  const handleSelectItem = (event: { target: HTMLSelectElement }) => {

    let thisItem:DataModel|undefined = undefined;

    let selectedId = parseInt(event.target.value);
    if(selectedId !== 0) {
      const findItem = availableItems.filter(item => item.itemId === selectedId);
      thisItem = findItem[0];
    }

    // Check which item is being changed
    switch(event.target.id) {
      case 'selection1':
        setSelection1(thisItem);
        break;
      case 'metric2Select':
        setSelection2(thisItem);
        break;
    }

  }


  const handleSubmit = (event: SyntheticEvent) => {

    event.preventDefault();

    // Validate

    let tempValid:ErrorHolder = INIT_VALIDATION;

    tempValid.valid = true;

    // Validate everything - set error if found
    if(selectedItems.length === 0) {    // There must be at least 1 selected item
      tempValid.valid = false;
      tempValid.selectedItems = "Please select at least one item";
    }
    // Make sure 2 different items are selected for selection 1 and selection 2
    if(!selection1) {
        tempValid.valid = false;
        tempValid.selection1 = "Please select two items to show on the chart";
    }else if(!selection2) {
        tempValid.valid = false;
        tempValid.selection2 = "Please select two items to show on the chart";
    }else if(selection1.itemId === selection2.itemId) {
        tempValid.valid = false;
        tempValid.selection2= "Please select two different items to show on the chart";
      }
    }

    setValid(tempValid);
  
  }

  return (
    <form onSubmit = { handleSubmit }>
      // List available options
      <div>{valid.selectedItems}</div> // Display error message for selected items
      <table>
        <thead>
          <tr>
            <td>Name</td>
            <td>Show</td>
          </tr>
        </thead>
        <tbody>
          {
          availableItems.map((item) => { // List all available items with checkboxes
            <tr key={item.itemId}>
              <td>{item.itemName}</td>
              <td><Checkbox value={item.itemId}
                            onChange={handleChangeItemSelected} 
                            checked={selectedItems.includes(item)} /></td>
            </tr>
          })
          }
        </tbody>
      </table>
      <select id = "selection1" onChange = { event => handleSelectItem(event) } 
              value = { selection1?.itemId}>
        <option key="sel1" value="0">Please Select</option>
        {
          availableItems.map((item) => <option key={item.itemId} value={item.itemId}>{item.itemName}</option>;
        }
      </select>
      <div>{valid.selection1}</div>
      <select id = "selection2" onChange = { event => handleSelectItem(event) } 
              value = { selection2?.itemId}>
        <option key="sel2" value="0">Please Select</option>
        {
          availableItems.map((item) => <option key={item.itemId} value={item.itemId}>{item.itemName}</option>;
        }
      </select>
      <div>{valid.selection2}</div>
    </form>
  );
  
}

export default Dashboard;

我遇到的问题是,如果我输入无效数据(例如,不选择任何内容,为 selection1 和 selection2 等选择相同的内容)并单击“提交”,错误不会显示。如果我做一个控制台日志,我可以看到该valid变量已正确设置,但似乎该组件没有重新渲染以显示它。

我究竟做错了什么?

标签: reactjstypescript

解决方案


在每次调用handleSubmit你时都会改变同一个对象INIT_VALIDATION,并且将同一个对象传递给setState函数不会触发重新渲染。

如果您通过替换复制

let tempValid:ErrorHolder = INIT_VALIDATION

const tempValid:ErrorHolder = {...INIT_VALIDATION}

我希望它能够工作。(这let不是问题的一部分,但如果您不更改变量本身,则可以使用 aconst代替。)


推荐阅读