簡體   English   中英

我正在使用 Redux。 我應該在 Redux 存儲中管理受控輸入狀態還是在組件級別使用 setState?

[英]I am using Redux. Should I manage controlled input state in the Redux store or use setState at the component level?

我一直試圖找出管理我的反應表單的最佳方法。 我嘗試使用 onChange 來觸發操作並使用我的表單數據更新我的 redux 存儲。 我也嘗試過創建本地狀態,當我的表單被提交時,我會觸發和操作並更新 redux 存儲。

我應該如何管理我的受控輸入狀態?

我喜歡 Redux 合著者之一的這個答案: https : //github.com/reactjs/redux/issues/1287

將 React 用於與全局應用無關且不會以復雜方式變異的臨時狀態。 例如,某個 UI 元素中的切換、表單輸入狀態。 將 Redux 用於全局重要或以復雜方式變異的狀態。 例如,緩存的用戶或帖子草稿。

有時你會想要從 Redux 狀態移動到 React 狀態(當在 Redux 中存儲一些東西變得尷尬時)或者相反(當更多組件需要訪問一些曾經是本地的狀態時)。

經驗法則是:做任何不那么尷尬的事情。

也就是說,如果您確定您的表單不會影響全局狀態或在您的組件卸載后需要保留,那么請保持在 react 狀態。

  1. 您可以使用組件自己的狀態。 然后將該狀態作為動作的參數。 這幾乎就是React Docs 中描述的“React 方式”。

  2. 您還可以查看Redux Form 它基本上完成了您所描述的工作,並將表單輸入與 Redux State 聯系起來。

第一種方法基本上意味着您正在手動完成所有操作 - 最大程度的控制和最大程度的樣板文件。 第二種方式意味着您讓高階組件為您完成所有工作。 然后就是介於兩者之間的一切。 我看到有多個包可以簡化表單管理的特定方面:

  1. React Forms - 它提供了一堆輔助組件,使表單渲染和驗證更加簡單。

  2. React JSON 模式- 允許從 JSON 模式構建 HTML 表單。

  3. Formsy React - 正如描述所說:“React JS 的這個擴展旨在成為靈活性和可重用性之間的‘甜蜜點’。”

更新:似乎這些天 Redux Form 被替換為:

  1. 反應最終形式

在該領域值得一試的另一個重要競爭者是:

  1. 福米克

在我的上一個項目中嘗試了 React Hook Form - 非常簡單,占用空間小,並且可以正常工作:

  1. 反應鈎形式

TL; 博士

可以使用任何看起來適合您的應用程序的內容(來源: Redux docs


確定應該將哪種數據放入 Redux 的一些常見經驗法則:

  • 應用程序的其他部分是否關心這些數據?
  • 您是否需要能夠基於這些原始數據創建進一步的派生數據?
  • 是否使用相同的數據來驅動多個組件?
  • 能夠將這種狀態恢復到給定的時間點(即時間旅行調試)對您是否有價值?
  • 您是否要緩存數據(即,如果它已經存在,則使用它的狀態而不是重新請求它)?

這些問題可以輕松幫助您確定更適合您的應用程序的方法。 以下是我在應用程序中使用的視圖和方法(用於表單):

本地狀態

  • 當我的表單與 UI 的其他組件沒有關系時很有用。 只需從input捕獲數據並提交。 我大部分時間使用它來處理簡單的表單。
  • 我在時間旅行調試表單輸入流方面沒有看到太多用例(除非其他一些 UI 組件依賴於此)。

Redux 狀態

  • 當表單必須更新我的應用程序中的其他一些 UI 組件時很有用(很像雙向綁定)。
  • 當我的表單input導致一些其他組件根據用戶輸入的內容render時,我會使用它。

就我個人而言,我強烈建議將所有內容保持在 Redux 狀態並遠離本地組件狀態。 這基本上是因為如果您開始將 ui 視為狀態的函數,您可以進行完整的無瀏覽器測試,並且您可以利用保留完整狀態歷史記錄的參考(例如,輸入中的內容,打開的對話框等) ,當一個錯誤發生時 - 不是他們從一開始的狀態是什么)供用戶調試。 來自 clojure 領域的相關推文

編輯添加:這是我們和我們的姊妹公司在我們的生產應用程序以及我們如何處理 redux/state/ui 方面的進展

使用幫助程序庫更快,避免我們所有的樣板。 它們可能會被優化、功能豐富……等等。 因為它們使所有不同的方面變得更加輕而易舉。 測試它們並讓你的武器庫知道什么對不同的需求有用和更好,這就是要做的事情。

但是如果你已經自己實現了一切 受控的道路 出於某種原因,您需要 redux 在我的一個項目中。 我需要維護表單狀態。 因此,如果我轉到另一個頁面並返回,它將保持相同的狀態。 您只需要 redux,如果它是將更改傳達給多個組件的一種方式。 或者,如果這是存儲狀態的一種方式,則您需要恢復。

如果狀態需要是全局的,則需要 redux。 否則你不需要它。 就此而言,您可以在此處查看這篇很棒的文章以進行深入了解。

您可能會遇到的問題之一! 使用受控輸入時。 是您可以在每次擊鍵時發送更改 您的表格將開始凍結 變成了蝸牛。

您永遠不應該在每次更改時直接調度和使用 redux 通量。 您可以做的是將輸入狀態存儲在組件本地狀態中。 並使用setState()更新。 一旦狀態發生變化,您就可以設置一個帶有延遲的計時器。 每次擊鍵時它都會被取消。 在指定的延遲之后,最后一次擊鍵將跟隨調度操作。 (好的延遲可能是 500 毫秒)。

(知道setState ,默認情況下可以有效地處理多個連續擊鍵。否則我們將使用與上述相同的技術(就像我們在 vanilla js 中所做的那樣)[但在這里我們將依賴setState ])

下面是一個例子:

onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        // here how you implement the delay timer
        clearTimeout(this.onInputsChangeTimeoutHandler); // we clear at ever keystroke
              // this handler is declared in the constructor
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
           // this will be executed only after the last keystroke (following the delay)
            if (typeof onInputsChange === "function")
                    onInputsChange(this.state.inputs, roomId, evt);
        }, 500);
    })
}

您可以使用反模式使用道具初始化組件,如下所示:

constructor(props) {
    super(props);

    const {
        name,
        description
    } = this.props;

    this.state = {
        inputs: {
            name,
            description
        }
    }

在構造函數或componentDidMount鈎子中,如下所示:

componentDidMount () {
    const {
        name, 
        description
    } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            name,
            description
        }
    });
}

后者允許我們在每個組件安裝時從商店恢復狀態。

此外,如果您需要從父組件更改表單,您可以向該父組件公開一個函數。 通過設置綁定的setInputs()方法。 在構造中,您執行 props(即 getter 方法) getSetInputs() (一個有用的情況是當您想在某些條件或狀態下重置表單時)。

constructor(props) {
    super(props);
    const {
         getSetInputs
    } = this.props;

   // .....
   if (typeof getSetInputs === 'function') getSetInputs(this.setInputs);
}

為了更好地理解我在上面所做的,這里是我如何更新輸入:

// inputs change handlers
onNameChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            name: value
        },
        evt
    );
}

onDescriptionChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            description: value
        },
        evt
    );
}

/**
 * change = {
 *      name: value
 * }
 */
onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        clearTimeout(this.onInputsChangeTimeoutHandler);
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
            if (typeof onInputsChange === "function")
                onInputsChange(change, roomId, evt);
        }, 500);
    })
}

這是我的表格:

 const {
        name='',
        description=''
 } = this.state.inputs;

// ....

<Form className="form">
    <Row form>
        <Col md={6}>
            <FormGroup>
                <Label>{t("Name")}</Label>
                <Input
                    type="text"
                    value={name}
                    disabled={state === "view"}
                    onChange={this.onNameChange}
                />
                {state !== "view" && (
                    <Fragment>
                        <FormFeedback
                            invalid={
                                errors.name
                                    ? "true"
                                    : "false"
                            }
                        >
                            {errors.name !== true
                                ? errors.name
                                : t(
                                        "You need to enter a no existing name"
                                    )}
                        </FormFeedback>
                        <FormText>
                            {t(
                                "Enter a unique name"
                            )}
                        </FormText>
                    </Fragment>
                )}
            </FormGroup>
        </Col>
        {/* <Col md={6}>
            <div className="image">Image go here (one day)</div>
        </Col> */}
    </Row>

    <FormGroup>
        <Label>{t("Description")}</Label>
        <Input
            type="textarea"
            value={description}
            disabled={state === "view"}
            onChange={this.onDescriptionChange}
        />
        {state !== "view" && (
            <FormFeedback
                invalid={
                    errors.description
                        ? "true"
                        : "false"
                }
            >
                {errors.description}
            </FormFeedback>
        )}
    </FormGroup>
</Form>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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