diff --git a/.changeset/dry-worms-invent.md b/.changeset/dry-worms-invent.md
new file mode 100644
index 00000000000..b41bb6d3065
--- /dev/null
+++ b/.changeset/dry-worms-invent.md
@@ -0,0 +1,6 @@
+---
+"@chakra-ui/editable": patch
+---
+
+Call `setPrevValue` `onFocus` to avoid an outdated prev value when the field is
+controlled
diff --git a/packages/editable/src/use-editable.ts b/packages/editable/src/use-editable.ts
index ca3f1f6e1f2..79dff1ffb22 100644
--- a/packages/editable/src/use-editable.ts
+++ b/packages/editable/src/use-editable.ts
@@ -161,6 +161,10 @@ export function useEditable(props: UseEditableProps = {}) {
}
}, [isInteractive])
+ const onUpdatePrevValue = useCallback(() => {
+ setPrevValue(value)
+ }, [value])
+
const onCancel = useCallback(() => {
setIsEditing(false)
setValue(prevValue)
@@ -247,7 +251,7 @@ export function useEditable(props: UseEditableProps = {}) {
hidden: isEditing,
"aria-disabled": ariaAttr(isDisabled),
tabIndex,
- onFocus: callAllHandlers(props.onFocus, onEdit),
+ onFocus: callAllHandlers(props.onFocus, onEdit, onUpdatePrevValue),
}
},
[
@@ -257,6 +261,7 @@ export function useEditable(props: UseEditableProps = {}) {
isPreviewFocusable,
isValueEmpty,
onEdit,
+ onUpdatePrevValue,
placeholder,
value,
],
@@ -277,8 +282,18 @@ export function useEditable(props: UseEditableProps = {}) {
onBlur: callAllHandlers(props.onBlur, onBlur),
onChange: callAllHandlers(props.onChange, onChange),
onKeyDown: callAllHandlers(props.onKeyDown, onKeyDown),
+ onFocus: callAllHandlers(props.onFocus, onUpdatePrevValue),
}),
- [isDisabled, isEditing, onBlur, onChange, onKeyDown, placeholder, value],
+ [
+ isDisabled,
+ isEditing,
+ onBlur,
+ onChange,
+ onKeyDown,
+ onUpdatePrevValue,
+ placeholder,
+ value,
+ ],
)
const getTextareaProps: PropGetterV2<
@@ -296,6 +311,7 @@ export function useEditable(props: UseEditableProps = {}) {
onBlur: callAllHandlers(props.onBlur, onBlur),
onChange: callAllHandlers(props.onChange, onChange),
onKeyDown: callAllHandlers(props.onKeyDown, onKeyDownWithoutSubmit),
+ onFocus: callAllHandlers(props.onFocus, onUpdatePrevValue),
}),
[
isDisabled,
@@ -303,6 +319,7 @@ export function useEditable(props: UseEditableProps = {}) {
onBlur,
onChange,
onKeyDownWithoutSubmit,
+ onUpdatePrevValue,
placeholder,
value,
],
diff --git a/packages/editable/stories/editable.stories.tsx b/packages/editable/stories/editable.stories.tsx
index 014805d9975..b9b848e91e5 100644
--- a/packages/editable/stories/editable.stories.tsx
+++ b/packages/editable/stories/editable.stories.tsx
@@ -8,6 +8,7 @@ import {
EditableTextarea,
useEditableControls,
} from "../src"
+import { Heading } from "@chakra-ui/layout"
export default {
title: "Components / Forms / Editable",
@@ -125,3 +126,37 @@ export const TextareaAsInput = () => {
)
}
+
+export const EditableEventHandler = () => {
+ const [name, setName] = React.useState("")
+ console.log("State 'name' is ", name)
+
+ React.useEffect(() => {
+ setName("John")
+ }, [])
+
+ return (
+ <>
+ Name State=[{name}]
+ {
+ console.log("onChange called with ", value)
+ setName(value)
+ }}
+ onSubmit={(value) => {
+ console.log("onSubmit called with ", value)
+ setName(value)
+ }}
+ onCancel={(value) => {
+ console.log("onCancel called with ", value)
+ setName(value)
+ }}
+ placeholder="Enter your name"
+ >
+
+
+
+ >
+ )
+}
diff --git a/packages/editable/tests/editable.test.tsx b/packages/editable/tests/editable.test.tsx
index 6e7e3abc04d..f826357e2dc 100644
--- a/packages/editable/tests/editable.test.tsx
+++ b/packages/editable/tests/editable.test.tsx
@@ -224,3 +224,56 @@ test("startWithEditView when true focuses on the input ", () => {
expect(document.activeElement === input).toBe(true)
})
+
+test.each([
+ { startWithEditView: true, text: undefined },
+ { startWithEditView: false, text: undefined },
+ { startWithEditView: true, text: "Bob" },
+ { startWithEditView: false, text: "Bob" },
+])(
+ "controlled: sets value toPrevValue onCancel, startWithEditView: $startWithEditView",
+ ({ startWithEditView, text }) => {
+ const Component = () => {
+ const [name, setName] = React.useState("")
+
+ React.useEffect(() => {
+ setName("John")
+ }, [])
+
+ return (
+ {
+ setName(value)
+ }}
+ onSubmit={(value) => {
+ setName(value)
+ }}
+ onCancel={(value) => {
+ setName(value)
+ }}
+ placeholder="Enter your name"
+ >
+
+
+
+ )
+ }
+
+ render()
+ const input = screen.getByTestId("input")
+ const preview = screen.getByTestId("preview")
+ if (!startWithEditView) {
+ fireEvent.focus(preview)
+ } else {
+ fireEvent.focus(input)
+ }
+ if (text) {
+ userEvent.type(input, text)
+ }
+ fireEvent.keyDown(input, { key: "Escape" })
+
+ expect(preview).toHaveTextContent("John")
+ },
+)
diff --git a/packages/editable/tests/editableTextarea.test.tsx b/packages/editable/tests/editableTextarea.test.tsx
index 6cd27f61231..1ac85adcefb 100644
--- a/packages/editable/tests/editableTextarea.test.tsx
+++ b/packages/editable/tests/editableTextarea.test.tsx
@@ -6,7 +6,12 @@ import {
userEvent,
} from "@chakra-ui/test-utils"
import * as React from "react"
-import { Editable, EditablePreview, EditableTextarea } from "../src"
+import {
+ Editable,
+ EditableInput,
+ EditablePreview,
+ EditableTextarea,
+} from "../src"
test("matches snapshot", () => {
render(
@@ -209,3 +214,55 @@ test("editable textarea can submit on blur", () => {
fireEvent.blur(textarea)
expect(onSubmit).toHaveBeenCalledWith("testing")
})
+test.each([
+ { startWithEditView: true, text: undefined },
+ { startWithEditView: false, text: undefined },
+ { startWithEditView: true, text: "Bob" },
+ { startWithEditView: false, text: "Bob" },
+])(
+ "controlled: sets value toPrevValue onCancel, startWithEditView: $startWithEditView",
+ ({ startWithEditView, text }) => {
+ const Component = () => {
+ const [name, setName] = React.useState("")
+
+ React.useEffect(() => {
+ setName("John")
+ }, [])
+
+ return (
+ {
+ setName(value)
+ }}
+ onSubmit={(value) => {
+ setName(value)
+ }}
+ onCancel={(value) => {
+ setName(value)
+ }}
+ placeholder="Enter your name"
+ >
+
+
+
+ )
+ }
+
+ render()
+ const input = screen.getByTestId("input")
+ const preview = screen.getByTestId("preview")
+ if (!startWithEditView) {
+ fireEvent.focus(preview)
+ } else {
+ fireEvent.focus(input)
+ }
+ if (text) {
+ userEvent.type(input, text)
+ }
+ fireEvent.keyDown(input, { key: "Escape" })
+
+ expect(preview).toHaveTextContent("John")
+ },
+)