![](/img/trans.png)
[英]Passing a function as a prop to a Typescript React Functional Component
[英]Passing state in function inside React functional component
总的来说,我对反应和打字稿还是很陌生,所以可能还有一些我没有看到的其他问题。 我发现的大多数教程都是针对基于类的组件而不是功能组件,这使得它变得更加困难。
我有一个包含两个复选框的组件。 切换复选框时,我还想将此更新发布到 url。 目前,切换正在工作,我能够相应地更新状态。 问题是在尝试发布更新时,请求中未设置更新状态,而是之前的状态。
下面是主要的Document
组件。 我认为问题出在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>
);
};
我只是希望能够将useState
提供的“状态”中的最新值传递给功能组件中的函数。
为了完整起见,我还将添加整个文件。 但基本上它只是一个围绕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>
);
}
更新:
使用@Ibz 提供的更改更新了Document
组件。 现在唯一的问题是,如果切换多个开关,则对更新 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>
);
};
更新 2:
下面是最终的工作代码,从 OP 稍微简化。 感谢@Ibz 的所有帮助!
关于重复的POST
请求:当我看到这个问题时,我正在使用yarn start
来运行开发服务器。 使用yarn build
并使用实际服务器提供文件后,问题不再存在。 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}
/>
);
};
使用 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 时,它会使用之前的切换值运行。
为了解决这个问题,我们通常会使用useEffect
。 这是一个钩子,每当其他值发生变化时,它就会运行一些代码。 在你的例子中,你会想要这样的:
useEffect(() => {
updateDocument('http://example.com/update', document.id, document.description, toggles.checked, toggles.expired)
}, [document.id, document.description, toggles.checked, toggles.expired])
useEffect 的第二个参数称为Dependency Array
,它是一个值列表,当更改时,会导致 useEffect 中的函数运行。
一开始围绕 state 可能有点棘手,但我希望这会有所帮助。 任何其他问题,只需发表评论。 您可以在此处找到更多信息: https : //reactjs.org/docs/hooks-effect.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.