簡體   English   中英

在 React 功能組件內的函數中傳遞狀態

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM