Javascript 在功能组件内部传递函数状态

Javascript 在功能组件内部传递函数状态,javascript,reactjs,typescript,semantic-ui-react,Javascript,Reactjs,Typescript,Semantic Ui React,一般来说,我对react和typescript还很陌生,所以可能还有一些其他问题我没有看到。我发现的大多数教程都是针对基于类的组件的,而不是针对函数式组件的,这使得它更加困难 我有一个包含两个复选框的组件。当切换复选框时,我也想将此更新发布到url。目前,切换正在工作,我可以相应地更新状态。问题是,当尝试发布更新时,请求中没有设置更新状态,而是设置了以前的状态 下面是主要的文档组件。我认为问题在于updateDocument函数,因为调用时状态不一定是由setToggles设置的。根据我所读到的

一般来说,我对react和typescript还很陌生,所以可能还有一些其他问题我没有看到。我发现的大多数教程都是针对基于类的组件的,而不是针对函数式组件的,这使得它更加困难

我有一个包含两个复选框的组件。当切换复选框时,我也想将此更新发布到url。目前,切换正在工作,我可以相应地更新状态。问题是,当尝试发布更新时,请求中没有设置更新状态,而是设置了以前的状态

下面是主要的
文档
组件。我认为问题在于
updateDocument
函数,因为调用时状态不一定是由
setToggles
设置的。根据我所读到的,我需要使用回调,但我不确定如何实现这一点

const Document: FC<{ document: IDocument }> = ({document}): ReactElement => {
    const [toggles, setToggles] = useState<DocumentToggles>(_documentToggles)

    const updateDocument = (uri: string, id: string, desc: string, checked: boolean, expired: boolean) => {
        axios.post(uri, {
            id: id,
            description: desc,
            checked: checked,
            expired: expired
        }).then(response => {
            console.log(response.data)
        });
    }

    const handleToggle = (e: FormEvent<HTMLInputElement>, data: any) => {
        console.log(data)
        if (e.currentTarget !== null) {
            const {name, checked} = data;
            setToggles(prevState => ({...prevState, [name]: checked}))
            // THIS IS NOT WORKING
            updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
        }
    }

    const handleSubmit = (e: FormEvent) => {
        if (e.currentTarget !== null) {
            e.preventDefault()
            updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
        }
    }

    return (
        <Container>
            <Form onSubmit={handleSubmit}>
                <DocumentCheckboxes
                    checked={toggles.checked}
                    expired={toggles.expired}
                    handleToggle={handleToggle}
                />
                <Form.Field>
                    <Button fluid type="submit">
                        Save
                    </Button>
                </Form.Field>
            </Form>
        </Container>
    );
};
更新:

使用@Ibz提供的更改更新了
文档
组件。现在唯一的问题是,如果切换多个切换,那么对更新url的
POST
请求将运行两次。仅切换单个组件将无法完成此操作

const Document: FC<{ document: IDocument }> = ({document}): ReactElement => {
    const [toggles, setToggles] = useState<DocumentToggles>(_documentToggles)

    const updateDocument = (uri: string, id: string, desc: string, checked: boolean, expired: boolean) => {
        axios.post(uri, {
            id: id,
            description: desc,
            checked: checked,
            expired: expired
        }).then(response => {
            console.log(response.data)
        });
    }

    const handleToggle = (e: FormEvent<HTMLInputElement>, data: any) => {
        console.log(data)
        if (e.currentTarget !== null) {
            e.preventDefault()

            setToggles(prevState => {
                const newState = {...prevState, [data.name]: data.checked};
                updateDocument('http://example.com/update', document.id, document.description, newState.checked, newState.expired);
                return newState;
            })
        }
    }

    const handleSubmit = (e: FormEvent) => {
        if (e.currentTarget !== null) {
            e.preventDefault()
            updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
        }
    }

    return (
        <Container>
            <Form onSubmit={handleSubmit}>
                <DocumentCheckboxes
                    checked={toggles.checked}
                    expired={toggles.expired}
                    handleToggle={handleToggle}
                />
                <Form.Field>
                    <Button fluid type="submit">
                        Save
                    </Button>
                </Form.Field>
            </Form>
        </Container>
    );
};

对于useState,不能保证操作是按顺序执行的,因为组件需要重新渲染以获得所有最新状态

setToggles(prevState => ({...prevState, [name]: checked}))
// THIS IS NOT WORKING
updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
这意味着使用这段代码,您的组件呈现,并且您在切换中有一些值。当您使用setToggles(…)时,react会将状态更新排队等待下一次渲染,因此当您使用updateDocument时,它将使用先前的toggles值运行

为了解决这个问题,我们通常使用
useffect
。这是一个钩子,每当其他值发生变化时,它就会运行一些代码。在您的实例中,您可能需要以下内容:

useEffect(() => {
  updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
}, [document.id, document.description, toggles.checked, toggles.expired])
useEffect的第二个参数称为
依赖项数组
,它是一个值列表,更改后,会导致useEffect中的函数运行


一开始在州里绞尽脑汁可能有点棘手,但我希望这能有所帮助。还有什么问题,请留言。您可以在这里找到更多信息:

({…prevState,[名称]:checked})
名称是否需要用大括号括起来?不幸的是,没有任何东西会立即向我解释为什么它会发送多个post请求。它是否在两个相同的请求时停止?或者数字是否随着每次切换而不断增加?我是在
文档
组件中使用
useffect
,还是在文档组件中使用
handleToggle
函数。React钩子需要在组件的顶层调用,并且通常在所有useState调用之后直接定义useEffect方法。因此,在您的例子中,在const[toggles,setToggles]=useState之后的一行中,根据我的理解,这会产生一个不同的问题。由于更新依赖项数组中的项时会调用useEffect,因此在呈现视图时,每个
文档
都会发出
POST
请求(在
updateDocument
中)。如果调用了
handleToggle
,我只想运行
updateDocument
。啊,我明白了。您可以尝试将函数传递到setState:
setToggles(prevState=>{const newState={…prevState,[name]:checked};updateDocument('http://example.com/update,document.id,document.description,newState.checked,newState.expired);return newState;})
。如果你喜欢的话,我可以把它作为一个单独的答案来发布,谢谢你的帮助!这几乎是工作,基本上是我想要的。但是有一个问题:在切换第二个切换时,无论是选中了
还是
过期了
,都会运行两次
更新文档
?我不太确定这是否确实来自
updateDocument
,但我可以看到有两个
POST
请求。仅切换一个切换时,仅进行一次
POST
。我已经编辑了OP,将更新的
文档
组件与您的建议一起包含在内。
setToggles(prevState => ({...prevState, [name]: checked}))
// THIS IS NOT WORKING
updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
useEffect(() => {
  updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
}, [document.id, document.description, toggles.checked, toggles.expired])