Formik组件拖拽排序后状态不更新问题求助
Hey there! Let's tackle those two Formik + react-sortly sync issues you're facing. Both problems boil down to not connecting your local component state with Formik's global state properly—let's fix that step by step.
Problem Breakdown
- Drag-sorting parents doesn't update Formik values: Your
PSortableTreeuses its own localuseStateto track parent nodes, so drag changes stay trapped in that component instead of updating Formik's state. - Deleting children doesn't refresh the drag area:
KSortableTreestores child items in local state, so when you delete a child via Formik's helpers, the drag area never gets the latest data from Formik.
Fixed Code Implementation
Here's the revised code that resolves both issues:
First, update your imports to include Formik's context hook:
import React from "react"; import ReactDOM from "react-dom"; import { Formik, FieldArray, useFormikContext } from "formik"; // Add useFormikContext import { Button, Form } from "antd"; import { DndProvider } from "react-dnd"; import HTML5Backend from "react-dnd-html5-backend"; import Sortly, { ContextProvider, useDrag, useDrop } from "react-sortly"; const FormItem = Form.Item;
Rewrite Item Renderers to Use Formik Context
const ItemRenderer = props => { const { index, data: { text, kids } } = props; const { setFieldValue, values } = useFormikContext(); // Access Formik state/methods const [, drag] = useDrag(); const [, drop] = useDrop(); return ( <FieldArray name="family.parents" render={arrayHelpers => ( <div ref={drop} style={{ width: "40vw" }} key={index}> <div ref={drag} style={{ padding: "0.5rem 1rem", marginBottom: ".5rem", backgroundColor: "green", cursor: "move", color: "white", width: "320px" }} > {text} <FormItem style={{ display: "inline-block" }}> <span className="parent-utility"> <Button className="ant-btn" shape="circle" size="small" style={{ color: "#ffa500" }} onClick={() => arrayHelpers.remove(index)} > Delete </Button> </span> </FormItem> </div> <KSortableTree position={index} values={values} setFieldValue={setFieldValue} /> </div> )} /> ); }; const ItemRendererA = props => { const { index, position, data: { text } } = props; const { setFieldValue } = useFormikContext(); const [, drag] = useDrag(); const [, drop] = useDrop(); return ( <FieldArray name={`family.parents[${position}].kids`} render={arrayHelpers => ( <> <div ref={drop} style={{ width: "40vw" }}> <div ref={drag} style={{ border: "1px dashed #70B5DC", padding: "0.5rem 1rem", marginBottom: ".5rem", marginLeft: "3rem", backgroundColor: "white", cursor: "move", color: "#70B5DC", width: "17rem" }} > {text} <FormItem style={{ display: "inline-block" }}> <span className="answer-utility"> <Button className="ant-btn" shape="circle" size="small" style={{ color: "#ffa500" }} onClick={() => arrayHelpers.remove(index)} > Delete </Button> </span> </FormItem> </div> </div> </> )} /> ); };
Remove Local State from Sortable Trees
const PSortableTree = ({ parentTree, setFieldValue }) => { const handleChange = newItems => { // Sync drag-sorted parents directly to Formik state setFieldValue("family.parents", newItems); }; return ( <Sortly items={parentTree} onChange={handleChange}> {props => <ItemRenderer {...props} />} </Sortly> ); }; const KSortableTree = ({ position, values, setFieldValue }) => { // Pull latest child items directly from Formik's state const itemsA = values.family.parents[position]?.kids || []; const handleChangeA = newItemsA => { // Sync drag-sorted children to the correct parent in Formik state setFieldValue(`family.parents[${position}].kids`, newItemsA); }; return ( <Sortly items={itemsA} onChange={handleChangeA}> {props => <ItemRendererA {...props} position={position} />} </Sortly> ); };
Update FormDetail to Pass Formik Methods
function FormDetail({ values, handleSubmit, setFieldValue }) { return ( <form onSubmit={handleSubmit}> <h3>Family Tree</h3> <DndProvider backend={HTML5Backend}> <ContextProvider> <PSortableTree parentTree={values.family.parents} setFieldValue={setFieldValue} /> </ContextProvider> </DndProvider> <div style={{ padding: 16, backgroundColor: "lightgrey", borderRadius: "8px", marginTop: "20px", maxWidth: "100vw" }} > {JSON.stringify(values)} </div> </form> ); };
Keep Your App Component as-is
function App() { return ( <Formik enableReinitialize initialValues={{ family: { id: "2da80396", parents: [ { depth: 0, id: "01a3b77c", kids: [ { depth: "1", id: "01a3b77d", text: "A", correct: true }, { depth: "1", id: "02fd6f62", text: "C", correct: false } ], text: "Parent 1" }, { depth: 0, id: "773a4170", kids: [ { depth: "1", id: "773a4171", text: "A", correct: false }, { depth: "1", id: "773a4172", text: "B", correct: true }, { depth: "1", id: "773a4173", text: "C", correct: false } ], text: "Parent 2" }, { depth: 0, id: "a3652131", kids: [ { depth: "1", id: "1c081c33", text: "G", correct: false }, { depth: "1", id: "a3654842", text: "A", correct: true }, { depth: "1", id: "a3654843", text: "B", correct: false } ], text: "Parent 3" }, { depth: 0, id: "a3654845", kids: [ { depth: "1", id: "a3654846", text: "A", correct: true }, { depth: "1", id: "a3654847", text: "B", correct: false } ], text: "Parent 4" }, { depth: 0, id: "eb3dc4d9", kids: [ { depth: "1", id: "2103b3bc", text: "D", correct: true }, { depth: "1", id: "28650f35", text: "A", correct: false }, { depth: "1", id: "699584c2", text: "B", correct: false }, { depth: "1", id: "a9edd5c4", text: "C", correct: false } ], text: "Parent 5" } ], text: "Heading" } }} onSubmit={values => alert(JSON.stringify(values))} component={FormDetail} /> ); }; const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
Key Fixes Explained
- Eliminated local state: We now pull all tree data directly from Formik's
values, so the drag area always reflects the latest form state. - Sync drag changes to Formik: When
Sortlydetects a drag reorder, we usesetFieldValueto update Formik's state immediately—no more disconnected data. - Used
useFormikContext: This hook lets us access Formik's state and methods anywhere in the component tree without messy prop drilling. - Auto-refresh on delete: Since the drag area is tied directly to Formik's values, deleting a node via
arrayHelpers.removeautomatically updates the UI.
内容的提问来源于stack exchange,提问作者Welsh
相关产品推荐
相关产品推荐

