React Hook "useEffect" 被有条件地调用,在假定简单的 get-display-retrieve 输入组件中

React Hook "useEffect" is called conditionally, in supposedly simple get-display-retrieve inputs component

请帮忙, 我在兜圈子(React 新手)。
我要么得到 'React Hook "useState" is called conditionally',如果我将其更改为下面的代码,
我得到 'React Hook "useEffect" is called conditionally'.

想法是

  1. 通过 useQuery 检索数据并通过 props 传入数据库记录 ID。
  2. 如果该数据为空,例如查询使用 id=0(就像插入而不是更新记录),然后 deviceObject 到空记录,否则到检索到的数据。
  3. 将 'deviceObject' 设置为状态。

即顺序很重要,但是 setRow 应该只被调用一次,而不是多次,这会导致渲染过多导致反应崩溃。

export default function DeviceModal(props) {
    const dataRowId = props.dataRowId;
    const classes = useStyles();
    const [row, setRow] = useState('')

    const device = useQuery(getDevice_query, {variables: {id: dataRowId}});
    if (device.loading) return <DataLoader/>;
    if (device.error) return <p style={{color: 'white'}}>{("GraphQL Error " + device.error)})</p>;

// Create an empty recordObject to be populated and sent back for insert to db.
    const emptyDevice = {
        id : 0,
        deviceId : 0,
        deviceClass :{name : '',},
        serialNumber: 0,
    }

    const deviceObject = device.data.getDevice !== null ? device.data.getDevice : emptyDevice;

    useEffect(()=>{
        setRow(deviceObject)
    },[])


    const handleSave = (value) => {
    };

    const HandleChange = e => {
        useEffect(()=>{
          setRow({...row, [e.target.name]: e.target.value })
    },[])
};

return (
    <div>
        <Modal ...>
         <div className={classes.paper}>
               <Grid container direction="row" justify="center"  alignItems="center">
                   <Grid item xs={4}>
                        <TextField
                            id="deviceId"
                            name="deviceId"
                            defaultValue={row.deviceId}
                            onChange={HandleChange}
                        />
                    </Grid>
                </Grid>
                {/* 30 other  textfields to capture*/}
                    ....
           </div>
        </Modal>
    </div>
)};

Edit as per Long Nguyen:

// Set the device object record to be either the empty record, or the records retrieved from db if those are populated (not null)    
let deviceObject = {};

const Component = () => {
    deviceObject = device.data.getDevice !== null ? device.data.getDevice: emptyDevice;
    return <RefactorComponent />;
}
   // Set the device object (empty or populated with db-retrieved rows,) into state
const RefactorComponent = () =>
{
     useEffect(()=>{
          setRow(deviceObject)
      },[deviceObject])
   // return ()
}
Component();

你在条件之后调用了钩子。它使您调用的钩子在渲染之间不会以相同的顺序出现,因为“React 依赖于调用钩子的顺序”。

您可以在本文档中找到有用的信息,以及解决问题的方法。

https://reactjs.org/docs/hooks-rules.html#explanation

更新:

  • 当然,你不应该把挂钩放在把手上。
  • 如果您仍想保留原始代码,可以像这样将条件后的块提取到其他组件
const Component = () => {
  if(conditions) return <Other />
  //  bad, this hook order is not fixed
  // it can appear or not
  useEffect(() => {
    ...the effect after condition...
  });

  return (...);
}

const Component = () => {
  if(conditions) return <Other />
  return <RefactorComponent />
}

const RefactorComponent = () => {
  // ✅ ok, the hooks order is fixed
  useEffect(() => {
    ...the effect after condition...
  });

  return (...);
}