有没有更简洁的方法在顶层定义 React Hooks useState?

Is there cleaner way to define React Hooks useState on the top level?

我有一个具有很多状态属性的向导组件。它用来保存大约5页内容的状态,然后控制提交到api。

在第一个例子中,我遵守了使用React的第一条规则 钩子。

1.仅在顶层调用挂钩

import React, { useState } from 'react'
import ReactDOM from 'react-dom'

const Wizard = props => {
    const [uuid, setUuid] = useState(props.uuid)
    const [firstName, setFirstName] = useState('')
    const [lastName, setLastName] = useState('')
    const [address1, setAddress1] = useState('')
    const [address2, setAddress2] = useState('')
    const [address3, setAddress3] = useState('')
    const [addressState, setAddressState] = useState('')
    //etc for many  

    return (
        <div>
           {/*
           <Page1 firstName={firstName} secondName={secondName} />
               //...more pages 
           */}
        </div>
    )
}

ReactDOM.render(<Wizard uuid={123} />, document.getElementById('root'))

我认为组件设置有点冗长,所以我想将所有 useState 调用放在一个不同的函数中(最终移动到一个单独的文件)并将其 return statesetters.

import React from 'react'
import ReactDOM from 'react-dom'

const generateState = props => {
    const [uuid, setUuid] = useState(props.uuid)
    const [firstName, setFirstName] = useState('')
    const [lastName, setLastName] = useState('')
    const [address1, setAddress1] = useState('')
    const [address2, setAddress2] = useState('')
    const [address3, setAddress3] = useState('')
    const [addressState, setAddressState] = useState('')
    //etc for many  

    return {
        state: {
            firstName,
            //etc
        },
        setters: {
            setFirstName,
            //etc
        }
    }
}

const Wizard = props => {

    const {
        state,
        setters
    } = generateState(props)

    return (
        <div>
            {/*
            <Page1 firstName={state.firstName} secondName={state.secondName} />
                //...more pages 
            */}
        </div>
    )
}

ReactDOM.render(<Wizard uuid={123}/>, document.getElementById('root'))

第二个例子打破了第一条规则,我想 "those are the rules!" 没问题。 我的问题是,是否有更清晰的方法来定义组件顶部的 useState 调用并避免错误:?

React Hook "useState" is called in function "generateState" which is neither a React function component or a custom React Hook function  react-hooks/rules-of-hooks

注意:我希望状态在组件中,而不是在其他地方(即 Redux)

您收到的 Lint 错误是因为您没有遵循 React 指南来创建自定义挂钩而不是命名 generateState 使用 useGenerateState 不会出现 linter 错误。

您在第二部分中所做的是创建自定义挂钩,只有当您希望它更干净时才正确,只需使用 useReducer 挂钩。

检查这个参考:

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

https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook

该案例代码将不会出现 linter 错误:

import React from 'react';
import ReactDOM from 'react-dom';

const useGenerateState = props => {
    const [uuid, setUuid] = useState(props.uuid)
    const [firstName, setFirstName] = useState('')
    const [lastName, setLastName] = useState('')
    const [address1, setAddress1] = useState('')
    const [address2, setAddress2] = useState('')
    const [address3, setAddress3] = useState('')
    const [addressState, setAddressState] = useState('')
    //etc for many  

    return {
        state: {
            firstName,
            //etc
        },
        setters: {
            setFirstName,
            //etc
        }
    }
}

const Wizard = props => {

    const {
        state,
        setters
    } = useGenerateState(props)

    return (
        <div>
            {/*
            <Page1 firstName={state.firstName} secondName={state.secondName} />
                //...more pages 
            */}
        </div>
    )
}

ReactDOM.render(<Wizard uuid={123}/>, document.getElementById('root'))

看来您正在使用 eslint 进行错误检查。

出现此错误的原因是自定义钩子的写入方式与推荐方式不一致。 官方的 reactjs 说自定义钩子应该 return 一个数组而不是一个对象。如果您可以将自定义挂钩更改为以下内容,错误应该会消失:

const generateState = props => {
const [uuid, setUuid] = useState(props.uuid)
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [address1, setAddress1] = useState('')
const [address2, setAddress2] = useState('')
const [address3, setAddress3] = useState('')
const [addressState, setAddressState] = useState('')
//etc for many  

return [uuid,setUuid,firstName,setFirstName,lastName,setLastName,address1,setAddress1,address2,setAddress2,address3,setAddress3,addressState,setAddressState]

}

如果您使用自定义挂钩,请像这样使用它:

   const [uuid,setUuid,firstName,setFirstName,lastName,setLastName,address1,setAddress1,address2,setAddress2,address3,setAddress3,addressState,setAddressState] = generateState(props)

在这种情况下是的,你可以将它移动到单独的钩子但是当我处理诸如表单等事情时我所做的是在这样的状态下使用object

const [formData, setFormData] = useState({
  uuid: '',
  firstName: '',
  lastName: '',
  address1: '',
  address2: '',
  address3: '',
  addressState, ''
});

使用上述方法设置这些单独值的唯一方法如下:

setFormData({ ...formData, lastName: value_of_last_name });

使用上述方法代码保持干净并遵循所有反应挂钩的规则。

关于您遇到的错误是因为 React Hooks 规则。

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks. more at: https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook

您可以使用 useReducer 挂钩。或者像下面这样使用平面状态对象。

const [state, setState] = useState({
  uuid: '',
  firstName: '',
  lastName: '',
  address1: '',
  address2: '',
  address3: '',
  addressState, ''
});