react-table-mapping ν¨ν€μ§λ₯Ό μκ°ν©λλ€!
μ΄μ©λ€ μ§μ λ§λ€μλ
μ¬ν΄ 5μ λΆν°, ν λ΄λΆμμ μ°¨μΈλ UI κ°νΈ νλ‘μ νΈ Version 2 λ₯Ό μ§ννλ©°, λͺ κ°μ λΌμ΄λΈλ¬λ¦¬κ° React 19 λ²μ μΌλ‘ μ¬λΌμ€λ©°, λ²μ νΈν λ¬Έμ λ‘ λμΌν λΌμ΄λΈλ¬λ¦¬λ₯Ό μ°λλ°μ μ μ½μ¬νμ΄ μμμ΅λλ€.
λ: νμ₯λ, μ ν¬ κΈ°μ‘΄ λΌμ΄λΈλ¬λ¦¬ λͺ κ°κ° React 19 λ²μ μΌλ‘ μ¬λΌμ€λ©΄μ, λ²μ νΈν
λ¬Έμ λ‘ λμΌν λΌμ΄λΈλ¬λ¦¬λ₯Ό μ°λλ°μ μ μ½μ¬νμ΄ μλλ° μ΄λ»κ² νλ©΄ μ’μκΉμ?
νμ₯λ: μ§μ λ§λ€μ΄λ΄.
λ: λ€.
λ: (λ€?)
λΉν©μ€λ¬μ μ§λ§, μ€νμμ€ ν¨ν€μ§λ₯Ό μ§μ λ§λ€μ΄λ³΄λ κ² λν μ¬λ°μ κ² κ°μ, μ§μ λ§λ€μ΄λ³΄κΈ°λ‘ νμμ΅λλ€. κ·Έλ κ² μμ±λ μ μ 첫λ²μ§Έ μ€νμμ€ ν¨ν€μ§λ λ°λ‘ !!
react-table-mapping μ΄λΌλ ν¨ν€μ§ μ
λλ€.
DEMO
κ°λ¨νκ² μμ μ§μ μ ν μ΄λΈμ κ° λ‘μ°μ, λ μ§μ μ ν μ΄λΈμ κ° λ‘μ°λ₯Ό λλκ·Έ μ€ λλ‘μΌλ‘ λ§€νν μ μλ GUI μ»΄ν¬λνΈμ λλ€. λ§μ μμ κ³Ό μνμ°©μ€κ° μμ΄ λ¬΄μν λ§μ λ² νλ²μ μ΄ μ‘΄μ¬νμ§λ§.. νμ¬λ ν΄λΉ λΌμ΄λΈλ¬λ¦¬λ₯Ό ν λ΄λΆμμ μ μ© μλ£ν μνλ©°, κ³§ λ²μ 1.0.0 μΌλ‘ λ°°ν¬ν μμ μ λλ€ π
λμ λ°©μ
μ΄λ€ λ°©μμΌλ‘ λμμ ν΄μΌν κΉ?
첫λ²μ§Έλ‘ ꡬνν λ°©μ - μμ‘΄μ± λ°°μ΄μ λ¬Έμ π₯
μ²μμλ λ¨μνΒ μ¬μ©μ κ΄μ μ΄ μλ, λΌμ΄λΈλ¬λ¦¬λ₯Ό λ§λλ μ μ₯μμλ§ μκ°νμ΅λλ€. νλ‘μ νΈ μ΄κΈ° λΉμ κΈ°κ°μ΄ μ΄λ°ν΄Β 빨리 λ§λ€μ΄μΌκ² λ€λ μκ°λ§Β κ°νλ κ² κ°μ΅λλ€.
λ§΅νμ νμνΒ μνλ₯Ό νΒ κ³³μμ κ΄λ¦¬νκΈ°Β μν΄Β Providerλ₯ΌΒ λ§λ€μ΄ μνλ₯Ό κ΄λ¦¬νμ΅λλ€.
κ·Έλ¦¬κ³ κ°μ λΌμΈ λ§΅ννκΈ°, λμΌν μ΄λ¦ λ§΅ννκΈ°, λΌμΈ λ€μ 그리기 λ± λͺ¨λ κΈ°λ₯μ 컀μ€ν ν μΌλ‘ λ§λ€μ΄ μ¬μ©μλ€μ΄ μΈ μ μκ² νμλ μ·¨μ§λ‘ μ§ννμ΅λλ€.
μΈ λ¬κ° μ΄μ¬ν κ°λ°νκ³ Β Providerλ₯Ό ν¬ν¨ν λΌμ΄λΈλ¬λ¦¬κ° μμ±λμμ΅λλ€.
μ΄λ₯Ό μ μ©νκΈ° μν΄ μΆκ·Όν΄μΒ npm registryμ λ°°ν¬λ ν¨ν€μ§λ₯Ό μ€μΉνκ³ μ μ©νλΒ μ€,Β Maximum rerender...Β λ°λ‘ 무ν 루νμΒ λΉ μ Έλ²λ Έμ΅λλ€.
μμΈμ μμ‘΄μ± λ°°μ΄ λ¬Έμ μμ΅λλ€.
useEffect(() => {
updateSourceFields(sourceFieldsFromProps)
}, [sourceFieldsFromProps]) // μ¬μ©νλ μͺ½μ μνκ° κ³μ λ°λκΈ° λλ¬Έμ 무ν루νλλ²μ§Έλ‘ ꡬνν λ°©μ - μ€κ³ μ€ν¨λ‘ μΈν μν λκΈ°ν λ¬Έμ π₯
κ·Έλ κ² λλ¬ν λ λ²μ§Έ κ²°κ³Όλ..
useEffect ν
μ μ¬μ©νλ, μμ‘΄μ± λ°°μ΄μ λΉ λ°°μ΄λ‘ μμ±νμ¬, μ΄κΈ° μν (sources, targets, initialMappings) λ§ μ£Όμ
νκ³ , λΌμ΄λΈλ¬λ¦¬ λ΄λΆμμ μνλ₯Ό λͺ¨λ κ΄λ¦¬νμ¬, onMappingChange λΌλ propsλ‘ λ³κ²½λ μνλ₯Ό μ μ μκ² νμ!
μμ΅λλ€.
<TableMappingProvider>
<TableMapping
lineType="bezier"
sourceColumns={sourceColumns}
targetColumns={targetColumns}
sources={sourceFields}
targets={targetFields}
initialMappings={initialMappings}
onMappingChange={(mappings) => {
console.info("mappings", mappings)
}}
/>
</TableMappingProvider>μλ₯Ό λ€μ΄ μμ κ°μ΄ μ¬μ©νλ©΄, λΌμ΄λΈλ¬λ¦¬ λ΄λΆμμ μνλ₯Ό λͺ¨λ κ΄λ¦¬νκ³ , onMappingChange λΌλ propsλ‘ λ³κ²½λ μνλ₯Ό μ μ μκ² νμμ΅λλ€.
μ΄λ κ² κ°μ νλ, μ΄κΈ° νΈμΆμλ 무ν 루νμ λΉ μ§μ§ μκ³ μ λμνμμ΅λλ€.
κ·Έλ¬λ..
onMappingChange={(mapping) =>
setSomethingState((prev) => ({ ...prev, mapping }))
}μμΒ κ°μ΄ μ¬μ©νλ©° μ¬μ©νλΒ μͺ½κ³Ό λΌμ΄λΈλ¬λ¦¬ λ΄λΆ μνμΒ μ±ν¬λ₯Ό λ§μΆλ©° κ°λ°νλ μ€,Β λΌμ΄λΈλ¬λ¦¬μμ μ 곡νλ hookμΌλ‘Β λΌμ΄λΈλ¬λ¦¬ λ΄λΆΒ μνμ μ¬μ©νλΒ μͺ½ μνλ₯Ό λμμ λ°κΎΈκ² λλ,
λ λ€μ μ°Ύμμ¨Β Maximum rerender...Β λ°λ‘ 무ν 루ν π
λ λ²μ§Έ μ΄μλ₯Ό κ²ͺκ³ λλ, μ²μμΌλ‘ λμκ° μ΄κΈ° μ€κ³λΆν° λ€μ μκ°ν΄λ³΄κ² λμμ΅λλ€.
μμΈ λΆμ
κ°λ°νκΈ° μ , μμ μκ² λμ‘λ μꡬμ¬νμ λ€μκ³Ό κ°μ΅λλ€.
- μ¬μ©μκ° λΌμ΄λΈλ¬λ¦¬ hookμ μ¬μ©νμ¬
μν λ³κ²½λ°Mapping UI λ³κ²½νμλ₯Ό ν μ μμμΌλ©΄ μ’κ² λ€. - λΌμ΄λΈλ¬λ¦¬ λ΄λΆ
mappingμνκ° λ³κ²½λ λ λ§λ€ μ¬μ©μκ° μ μ μμμΌλ©΄ μ’κ² λ€. - λνμ μΈ μ‘μ
(mapping, update, remove)μμ, μ μ²λ¦¬, νμ²λ¦¬ κΈ°λ₯μ΄ μμμΌλ©΄ μ’κ² λ€.
κ·Έλ¦¬κ³ κ΅¬μνλ νλ‘μΈμ€λ μλμ κ°μ΅λλ€.
ꡬμνλ νλ‘μΈμ€ π
-
μ¬μ©μ μ΄λ²€νΈ
(λλκ·Έ&λλ‘, onChange λ±)β
-
λΌμ΄λΈλ¬λ¦¬ λ΄λΆ μν λ³κ²½
β
-
propsλ‘ μ λ¬ λ°μ
onMappingChangeμ½λ°± νΈμΆβ
-
λΆλͺ¨ μ»΄ν¬λνΈμ μν μ λ°μ΄νΈ
(setSomethingState)β
-
UI λ³κ²½
λ¬Έμ κ° λλ νλ‘μΈμ€ π
-
μ¬μ©μ μ΄λ²€νΈ λ°μ
β
-
λΌμ΄λΈλ¬λ¦¬μμ μ 곡νλ hook νΈμΆ
(ex: onBeforeMappingChange)β
-
hookμμ λΌμ΄λΈλ¬λ¦¬ λ΄λΆ μν λ³κ²½ + λΆλͺ¨ μν λ³κ²½ λμ λ°μ
β
-
onMappingChangeμ½λ°± νΈμΆβ
-
λΆλͺ¨ μ»΄ν¬λνΈ μν μ λ°μ΄νΈ (setSomethingState)β
-
λΆλͺ¨ 리λ λλ§μΌλ‘ μλ‘μ΄ props μ λ¬β
-
λΌμ΄λΈλ¬λ¦¬ λ΄λΆμμ props λ³κ²½ κ°μ§
β
-
λ€μ μν λκΈ°ν μλ
β
-
7 ~ 8λ²νλ‘μΈμ€ 무ν λ°λ³΅β
-
Maximum rerenderβ¦
λ¬Έμ μ λΆμ
μμΈμ λΆμνκ³ λμ, λ€λ₯Έ λΌμ΄λΈλ¬λ¦¬μ μνλ μ΄λ»κ² κ΄λ¦¬λκ³ μμκΉβ¦ μκ°νκ² λμμ΅λλ€.
κ·Έλ¦¬κ³ μμ£Ό μ¬μ©νλ λΌμ΄λΈλ¬λ¦¬μΈ react-hook-form μ΄λΌλ ν¨ν€μ§κ° λ μ¬λκ³ , μ μ΄ μ»΄ν¬λνΈμ λΉμ μ΄ μ»΄ν¬λνΈ κ΄μ μμ μκ°ν΄λ³΄κ² λμμ΅λλ€.
ν΅μ¬ λ¬Έμ μ λ€
- μν μμ κΆμ λͺ¨νΈν¨μ΄ μ‘΄μ¬νμ΅λλ€.
// λΌμ΄λΈλ¬λ¦¬ λ΄λΆ μν
const [internalMappings, setInternalMappings] = useState(initialMappings)
// μ¬μ©μ μΈ‘ μν
const [userMappings, setUserMappings] = useState(initialMappings)
// λ¬Έμ : μν λκΈ°νκ° νλ€κ³ , 무νλ λμ μ£ΌμμμΈμ΄ λμμ΅λλ€.μ΄λ€ μνκ° μ€μ λ°μ΄ν°μΈμ§ λͺ ννμ§ μμκΈ°μ, λ μν κ° λκΈ°ν κ³Όμ μμ λ§μ μΆ©λμ΄ λ°μνμ΅λλ€.
- λλ λͺ¨λ₯΄κ² React μμ μλ°©ν₯ λ°μ΄ν° λ°μΈλ©μ μλν΄λ²λ¦° μΌμ΄μ€
1λ²μ λ¬Έμ μ μ νμ
νκ³ λλ, λ¨λ°©ν₯ λ°μ΄ν° νλ¦μ μ§ν₯νλ Reactμμ μμ λ λͺ¨λ₯΄κ² μλ°©ν₯ λ°μ΄ν° λ°μΈλ©μ μΆκ΅¬ν΄λ²λ Έλ€κ³ μκ°λ λ€μμ΅λλ€.
//library hooks
const { updateSourceFields } = useTableMapping()
//local state
const [newSourceFields, setNewSourceFields] = useState<SourceFields>({
//..
})
const handleSomeAction = (sourceFields: SourceFields) => {
updateSourceFields(sourceFields) // 1. λΌμ΄λΈλ¬λ¦¬ λ΄λΆ μν λ³κ²½
setNewSourceFields(sourceFields) // 2. μ¬μ©μ μΈ‘ μ§μ μν λ³κ²½
}μμ μ½λμ κ°μ΄ μμ±νκ³ μμλλ°, μλ°©ν₯μΌλ‘ μνλ₯Ό λκΈ°ννλ €λ€ λ³΄λ, μν λ³κ²½μ΄ μ°μμ μΌλ‘ λ°μνμ¬ λ¬΄ν 루νμ μ£Όλ μμΈμ΄ λμλ κ² κ°μμ΅λλ€.
λ΄λ¦° κ²°λ‘
μ·¨μ
μ€λΉ μμ μ¬μ©ν΄λ΄€λ μν κ΄λ¦¬ λΌμ΄λΈλ¬λ¦¬μΈ Redux λΌλ ν¨ν€μ§λ₯Ό μκ°ν΄λ³΄κ² λμμ΅λλ€.
Reduxλ View μ»΄ν¬λνΈμμ μ μ μνλ₯Ό ꡬλ
νκ³ , Dispatch ν¨μλ₯Ό ν΅ν΄ νΉμ Action typeκ³Ό λ³κ²½ν μν, μ¦ payloadλ₯Ό 보λ΄μ΄ λ°μ΄ν°λ₯Ό μ
λ°μ΄νΈ νλ Flux μν€ν
μ²λ₯Ό μ§ν₯ν©λλ€.
Action β Dispatcher β Store β View
β β
ββββββββ User Interaction βββ
Flux μν€ν
μ² μ ν΅μ¬μ λ¨λ°©ν₯ λ°μ΄ν° νλ¦ μ΄κΈ°μ, λΌμ΄λΈλ¬λ¦¬μ λΉμ·ν νμμΌλ‘ μ μ©ν΄λ³΄κΈ°λ‘ νμμ΅λλ€.
μ μ©ν΄λ³΄κΈ°
Hook μ 곡 λ°©μ λ³κ²½
κΈ°μ‘΄ useTableMappingμ νΈμΆνμ¬ hookμ μ¬μ©νλ λ°©μμμ, refλ₯Ό λ°μ μ¬μ©νλ λ°©μμΌλ‘ κ°μ νμμ΅λλ€.
μ΄μ λ μλμ κ°μμ΅λλ€.
Providerμ κ±°λ‘ μΈνuseTableMappingμ μ¬μ©νκ³ μλ μ»΄ν¬λνΈμ λΆνμν λ λλ§ λ°©μ§λ κ²μ΄λ€..- 보μΌλ¬ νλ μ΄νΈ μ½λκ° κ°μν κ²μ΄λ€.
- λΆλͺ¨ μ»΄ν¬λνΈμμ μ§μ μ μΌλ‘ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ μ΄νλ©°, νΈμΆν΄λ
onMappingChangeλ‘ λ³κ²½λ μνλ§ μ λ¬λκΈ° λλ¬Έμ, 무νλ λλ§μ λ°©μ§ν μ μμ κ²μ΄λ€
<AddButton
variant="outline"
onClick={() => {
if (tableMappingRef.current) {
tableMappingRef.current.appendSource(generateSourceDefaultValue());
}
}}
>μ€μ λ‘ μμ κ°μ΄ μ¬μ©ν μ μκ² λμμ΅λλ€ π
μν κ΄λ¦¬μ κ°μ
λ°μ΄ν°κ° λ¨λ°©ν₯μΌλ‘ νλ₯Ό μ μκ² κ°μ νμμ΅λλ€.
λΌμ΄λΈλ¬λ¦¬ λ΄λΆμμλ μνλ₯Ό κ΄λ¦¬νμ§ μμΌλ©°, νΉμ Actionμ΄ μΌμ΄λ¬μ μ, Action typeκ³Ό λ³κ²½λ μνλ₯Ό λ΄κ³ μλ Payloadλ₯Ό μ¬μ©νλ μΈ‘μΌλ‘ μ λ¬ν©λλ€.
const useTableMapping = ({
sources: sourcesFromProps,
targets: targetsFromProps,
mappings: mappingsFromProps,
onStateChange,
}: UseTableMappingProps) => {
const sourceFields = sourcesFromProps as FieldItem[]
const targetFields = targetsFromProps as FieldItem[]
const mappings = mappingsFromProps
const notifyChange = (
newSources: FieldItem[],
newTargets: FieldItem[],
newMappings: Mapping[],
action: NotifyAction,
) => {
onStateChange({
sources: newSources,
targets: newTargets,
mappings: newMappings,
action,
})
}
//...
}μμ κ°μ΄, λΌμ΄λΈλ¬λ¦¬μμ μ 곡νλ apiλ₯Ό νΈμΆνκ±°λ, λΌμ΄λΈλ¬λ¦¬ λ΄λΆμμ μν λ³νκ° μΌμ΄λ¬μ μ, λ΄λΆμ μΌλ‘ notifyChangeλΌλ ν¨μλ₯Ό νΈμΆνκ³ , μ΅μ’
μ μΌλ‘ onMappingChange λ‘λ§ λ³κ²½λ μνκ° μ λ¬λ©λλ€
μλ₯Ό λ€μ΄, λͺ¨λ λ§΅νμ μ κ±°νκΈ° μν΄ λ€μκ³Ό κ°μ΄ νΈμΆνκ² λμμ΅λλ€.
<TrashButton
variant="outline"
className="border-[var(--color-border-default)]"
onClick={() => {
if (tableMappingRef.current) {
tableMappingRef.current.clearMappings();
}
}}
>κ·Έλ¬λ©΄ onMappingChangeλ‘ μλμ κ°μ μνκ° μ λ¬λκ² λ©λλ€ π
<TableMapping
//..
onMappingChange={(stateWithAction) => {
const { sources, targets, mappings, action } = stateWithAction;
Β Β console.log(stateWithAction);
// sources: λ³κ²½λ sources
// targets: λ³κ²½λ targets
// mappings: λ³κ²½λ mappings
// action: {
// type: 'REMOVE_MAPPING',
// paload: {
// mappingId: string;
Β Β //Β Β removedMapping: Mapping | undefined;
// }
// }
Β }
/>κ·Έλ κ² μμ±λ κ²°κ³Όλ¬Ό .. π
νμ±ν μν
λΉνμ±ν μν
λλ μ π€΅
μ΄λ²Β λΌμ΄λΈλ¬λ¦¬Β κ°λ°μΒ ν΅ν΄Β ReactμΒ μνΒ κ΄λ¦¬μ λν΄ λ κΉκ² μκ°ν΄λ³΄κ² λ κ³κΈ°κ° λμμ΅λλ€.Β μ²μμλΒ λ¨μνΒ "빨리 λ§λ€μ΄μΌΒ νλ€" λ μκ°μΌλ‘ μ κ·Όνμ§λ§, 무νΒ λ λλ§μ΄λΌλΒ μ΄μμΒ λΆλͺνλ©΄μ κ·Όλ³Έμ μΈΒ μ€κ³μΒ μ€μμ±μΒ κΉ¨λ¬μμ΅λλ€.
νΉν λ¨λ°©ν₯ λ°μ΄ν° νλ¦μμμΒ μνλ₯Ό λκ° κ΄λ¦¬νλκ° λΌλ κ°λ
μ΄Β μΌλ§λΒ μ€μνμ§Β μκ²Β λμμ΅λλ€.
μ΄κΈ°μ μ€κ³νλ ꡬ쑰λ, κ°μ ν μκ°ν΄λ³΄λ μ¬λ λ λͺ μ΄, μλμ°¨ νΈλ€μ μ₯κ³ μλ‘ μ’μ°λ‘ λ리λ λλμ΄μλ κ² κ°μ΅λλ€.
μ΄λ² react-table-mapping ν¨ν€μ§ κ°μ μ΄ν, λ€λ₯Έ ν¨ν€μ§λ₯Ό λ§λ€κ² λλ λ μ΄ μ€λ©΄, μ²μλΆν°Β λͺ
νν μν κ΄λ¦¬Β μ λ΅μ μ립νκ³ , μ¬μ©μ κ΄μ μμμΒ κ°λ°μ κ²½νμΒ μ°μ μ μΌλ‘ κ³ λ €ν΄μΌκ² λ€λΒ λ€μ§μ νμμ΅λλ€.
μ΄μμΌλ‘ μ€λ ν¬μ€ν μ λ§μΉκ² μ΅λλ€ !