[英]Passing state in function inside React functional component
I am still pretty new to react and typescript in general, so there might be some other issues I am not seeing.总的来说,我对反应和打字稿还是很陌生,所以可能还有一些我没有看到的其他问题。 Most of the tutorials I am finding are for class based components instead of functional ones, making it more difficult.我发现的大多数教程都是针对基于类的组件而不是功能组件,这使得它变得更加困难。
I have a component that contains two checkboxes.我有一个包含两个复选框的组件。 When toggling the checkbox, I would also like to post this update to a url.切换复选框时,我还想将此更新发布到 url。 Currently, the toggles are working and I am able to update the state accordingly.目前,切换正在工作,我能够相应地更新状态。 The issue is when attempting to post the update, the updated state is not set in the request, but rather the previous state.问题是在尝试发布更新时,请求中未设置更新状态,而是之前的状态。
Below is the main Document
component.下面是主要的Document
组件。 I think the issue is with the updateDocument
function, since the state has not necessarily been set by setToggles
when it is called.我认为问题出在updateDocument
函数上,因为setToggles
在调用它时不一定会设置状态。 From what I have read, I need to use a callback, but I am unsure how I would implement this.从我读到的内容来看,我需要使用回调,但我不确定如何实现这一点。
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>
);
};
I just want to be able to pass up-to date values from the "state" provided by useState
to a function within a functional component.我只是希望能够将useState
提供的“状态”中的最新值传递给功能组件中的函数。
I will also add the whole file for the sake of completeness.为了完整起见,我还将添加整个文件。 But basically it is just a wrapper component around an array of Document
s:但基本上它只是一个围绕Document
数组的包装组件:
const _DOCS: IDocument[] = [{id: "666666666666", description: "TESTDESC", checked: false, expired: false}]
const MainAppView = () => {
return (
<div>
<DocumentViewBox documents={_DOCS}/>
</div>
);
}
interface IDocument {
id: string;
description: string;
checked: boolean;
expired: boolean;
}
// DocumentViewBox is used to display a list of documents.
const DocumentViewBox: FC<{ documents: IDocument[] }> = ({documents}): ReactElement => {
return (
<div>
{documents.map(doc => {
return <Document key={doc.id} document={doc}/>
})}
</div>
);
};
interface DocumentToggles {
checked: boolean;
expired: boolean;
}
const _documentToggles: DocumentToggles = {checked: false, expired: false}
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>
);
};
const DocumentCheckboxes: FC<{ checked: boolean, expired: boolean, handleToggle: (e: FormEvent<HTMLInputElement>, data: any) => void }> = ({checked, expired, handleToggle}): ReactElement => {
return (
<Container textAlign="left">
<Divider hidden fitted/>
<Checkbox
toggle
label="Checked"
name="checked"
onChange={handleToggle}
checked={checked}
/>
<Divider hidden/>
<Checkbox
toggle
label="Expired"
name="expired"
onChange={handleToggle}
checked={expired}
/>
<Divider hidden fitted/>
</Container>
);
}
UPDATE:更新:
Updated Document
component with the change provided by @Ibz.使用@Ibz 提供的更改更新了Document
组件。 The only issue now is that the POST
request to the update url is run twice if multiple toggles are toggled.现在唯一的问题是,如果切换多个开关,则对更新 url 的POST
请求会运行两次。 Toggling only a single component will not do this.仅切换单个组件不会执行此操作。
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>
);
};
UPDATE 2:更新 2:
Below is the final working code, slightly simplified from the OP.下面是最终的工作代码,从 OP 稍微简化。 Thanks to @Ibz for all the help!感谢@Ibz 的所有帮助!
Regarding the duplicate POST
requests: I was using yarn start
to run a development server when I was seeing this issue.关于重复的POST
请求:当我看到这个问题时,我正在使用yarn start
来运行开发服务器。 After building with yarn build
and serving the files with the actual server, the issue is no longer present.使用yarn build
并使用实际服务器提供文件后,问题不再存在。 This answer on the axios
issues page made me try this. axios
问题页面上的这个答案让我尝试了这个。
import React, {Component, useState, useEffect, FC, ReactElement, MouseEvent, FormEvent, ChangeEvent} from "react";
import {Container, Segment, Label, Checkbox, CheckboxProps} from "semantic-ui-react";
import axios from "axios";
interface IDocument {
id: string;
description: string;
checked: boolean;
expired: boolean;
}
const _DOCS: IDocument[] = [{id: '0', description: '', checked: false, expired: false}]
const MainAppView = () => {
return (
<div>
<DocumentViewBox documents={_DOCS}/>
</div>
);
}
const DocumentViewBox: FC<{ documents: IDocument[] }> = ({documents}): ReactElement => {
return (
<div>
{documents.map(doc => <Document key={doc.id} document={doc}/>)}
</div>
);
};
const defaultDocumentProps: IDocument = {id: '', description: '', checked: false, expired: false};
const Document: FC<{ document: IDocument }> = ({document}): ReactElement => {
const [documentProps, setDocumentProps] = useState<IDocument>(defaultDocumentProps);
// Run only once and set data from doc
// as the initial state.
useEffect(() => {
setDocumentProps(document)
}, []);
const updateDocument = (uri: string, updateDoc: IDocument) => {
axios.post(uri, updateDoc).then(response => {
console.log('updateDocument response:')
console.log(response.data)
}).catch(err => {
console.log('updateDocument error:' + err)
});
}
const handleToggle = (e: FormEvent<HTMLInputElement>, data: CheckboxProps) => {
e.preventDefault()
setDocumentProps(prevState => {
const {name, checked} = data;
const newState = {...prevState, [name as string]: checked};
console.log('handleToggle new state:')
console.log(newState)
updateDocument('http://example.com/update', newState);
return newState;
});
}
return (
<Checkbox
toggle
label='Checked'
name='checked'
onChange={handleToggle}
checked={documentProps.checked}
/>
);
};
With useState, there's no guarantee that the action is executed in order, as your component needs to re-render to have all the up to date state.使用 useState,不能保证操作按顺序执行,因为您的组件需要重新渲染以拥有所有最新状态。
setToggles(prevState => ({...prevState, [name]: checked}))
// THIS IS NOT WORKING
updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
That means with this piece of code, your component renders, and you have some value in toggles.这意味着使用这段代码,您的组件会呈现,并且您在切换中有一些价值。 When you get to setToggles(...), react queues the update of the state for the next render, so when you get to updateDocument, it's being run with the previous value of toggles.当您到达 setToggles(...) 时,react 会将状态更新排入下一次渲染的队列,因此当您到达 updateDocument 时,它会使用之前的切换值运行。
To get around this, we would usually use useEffect
.为了解决这个问题,我们通常会使用useEffect
。 This is a hook which runs some code whenever some other value changes.这是一个钩子,每当其他值发生变化时,它就会运行一些代码。 In your instance, you would want something like:在你的例子中,你会想要这样的:
useEffect(() => {
updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
}, [document.id, document.description, toggles.checked, toggles.expired])
The second argument to useEffect is called the Dependency Array
, and is a list of values that when changed, causes the function inside useEffect to run. useEffect 的第二个参数称为Dependency Array
,它是一个值列表,当更改时,会导致 useEffect 中的函数运行。
It can be a little tricky wraping your head around state at first, but I hope this helped.一开始围绕 state 可能有点棘手,但我希望这会有所帮助。 Any other questions, just leave a comment.任何其他问题,只需发表评论。 You can find more information here: https://reactjs.org/docs/hooks-effect.html您可以在此处找到更多信息: https : //reactjs.org/docs/hooks-effect.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.