Skip to content

Commit 5eaece3

Browse files
committed
fix: Fix onTabClick props issue.
1 parent 3c10900 commit 5eaece3

File tree

5 files changed

+136
-38
lines changed

5 files changed

+136
-38
lines changed

core/README.md

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,22 @@ Draggable tabs for React. Demo Preview: [@uiwjs.github.io/react-tabs-draggable](
1414
npm install @uiw/react-tabs-draggable --save
1515
```
1616

17-
## Usage
17+
## Base Usage
1818

1919
```jsx mdx:preview
2020
import React, { useState } from 'react';
2121
import Tabs, { Tab } from '@uiw/react-tabs-draggable';
22-
import styled from 'styled-components';
23-
24-
const Content = styled.div`
25-
border-top: 1px solid #333;
26-
`;
2722

2823
function App() {
2924
const [activeKey, setActiveKey] = useState('tab-1');
3025
return (
3126
<div>
32-
<Tabs activeKey={activeKey} style={{ gap: 6 }} onTabClick={(id) => setActiveKey(id)}>
33-
<Tab id="tab-1">Google</Tab>
34-
<Tab id="tab-2">MicroSoft</Tab>
35-
<Tab id="tab-3">Baidu</Tab>
36-
<Tab id="tab-4">Taobao</Tab>
37-
<Tab id="tab-5">JD</Tab>
27+
<Tabs activeKey={activeKey} style={{ gap: 12 }} onTabClick={(id) => setActiveKey(id)}>
28+
<Tab id="tab-1">{activeKey === 'tab-1' && ''}Google</Tab>
29+
<Tab id="tab-2">{activeKey === 'tab-2' && ''}MicroSoft</Tab>
30+
<Tab id="tab-3">{activeKey === 'tab-3' && ''}Baidu</Tab>
31+
<Tab id="tab-4">{activeKey === 'tab-4' && ''}Taobao</Tab>
32+
<Tab id="tab-5">{activeKey === 'tab-5' && ''}JD</Tab>
3833
</Tabs>
3934
<div style={{ background: '#fff', padding: 12 }}>{activeKey}</div>
4035
</div>
@@ -52,7 +47,7 @@ import React, { useState } from 'react';
5247
import Tabs, { Tab } from '@uiw/react-tabs-draggable';
5348
import styled from 'styled-components';
5449

55-
const TabIten = styled(Tab)`
50+
const TabItem = styled(Tab)`
5651
background-color: #b9b9b9;
5752
padding: 3px 7px;
5853
border-radius: 5px 5px 0 0;
@@ -71,11 +66,11 @@ function App() {
7166
return (
7267
<div>
7368
<Tabs activeKey={activeKey} style={{ gap: 6 }} onTabClick={(id) => setActiveKey(id)}>
74-
<TabIten id="tab-0-1">Google</TabIten>
75-
<TabIten id="tab-0-2">MicroSoft</TabIten>
76-
<TabIten id="tab-0-3">Baidu</TabIten>
77-
<TabIten id="tab-0-4">Taobao</TabIten>
78-
<TabIten id="tab-0-5">JD</TabIten>
69+
<TabItem id="tab-0-1">Google</TabItem>
70+
<TabItem id="tab-0-2">MicroSoft</TabItem>
71+
<TabItem id="tab-0-3">Baidu</TabItem>
72+
<TabItem id="tab-0-4">Taobao</TabItem>
73+
<TabItem id="tab-0-5">JD</TabItem>
7974
</Tabs>
8075
<Content>{activeKey}</Content>
8176
</div>
@@ -93,7 +88,89 @@ import React, { Fragment, useState, useCallback } from 'react';
9388
import Tabs, { Tab, useDataContext } from '@uiw/react-tabs-draggable';
9489
import styled from 'styled-components';
9590

96-
const TabIten = styled(Tab)`
91+
const TabItem = styled(Tab)`
92+
background-color: #b9b9b9;
93+
padding: 3px 7px;
94+
border-radius: 5px 5px 0 0;
95+
user-select: none;
96+
&.w-active {
97+
color: #fff;
98+
background-color: #333;
99+
}
100+
`;
101+
102+
const Content = styled.div`
103+
border-top: 1px solid #333;
104+
`;
105+
106+
function insertAndShift(arr, from, to) {
107+
let cutOut = arr.splice(from, 1)[0];
108+
arr.splice(to, 0, cutOut);
109+
return arr;
110+
}
111+
112+
let count = 5;
113+
114+
function App() {
115+
const [data, setData] = useState([
116+
{ id: 'tab-3-1', children: 'Google' },
117+
{ id: 'tab-3-2', children: 'MicroSoft' },
118+
{ id: 'tab-3-3', children: 'Baidu' },
119+
{ id: 'tab-3-4', children: 'Taobao' },
120+
{ id: 'tab-3-5', children: 'JD' },
121+
]);
122+
const [test, setTest] = useState(1);
123+
const [activeKey, setActiveKey] = useState('');
124+
125+
const tabClick = (id, evn) => {
126+
evn.stopPropagation();
127+
setActiveKey(id);
128+
setTest(test + 1);
129+
};
130+
const closeHandle = (item, evn) => {
131+
evn.stopPropagation();
132+
setData(data.filter((m) => m.id !== item.id));
133+
};
134+
const addHandle = () => {
135+
++count;
136+
const newData = [...data, { id: `tab-3-${count}`, children: `New Tab ${count}` }];
137+
setData(newData);
138+
};
139+
const tabDrop = (id, index) => {
140+
const oldIndex = [...data].findIndex((m) => m.id === id);
141+
const newData = insertAndShift([...data], oldIndex, index);
142+
setData(newData);
143+
};
144+
return (
145+
<Fragment>
146+
<button onClick={addHandle}>Add{test}</button>
147+
<Tabs
148+
style={{ gap: 3, overflow: 'auto' }}
149+
onTabClick={(id, evn) => tabClick(id, evn)}
150+
onTabDrop={(id, index) => tabDrop(id, index)}
151+
>
152+
{data.map((m, idx) => {
153+
return (
154+
<TabItem key={idx} id={m.id} draggable={idx !== 0}>
155+
{m.children}
156+
<button onClick={(evn) => closeHandle(m, evn)}>x</button>
157+
</TabItem>
158+
);
159+
})}
160+
</Tabs>
161+
<Content>{activeKey}</Content>
162+
</Fragment>
163+
);
164+
}
165+
export default App;
166+
```
167+
168+
```jsx mdx:preview
169+
import React, { Fragment, useState, useCallback } from 'react';
170+
import Tabs, { Tab, useDataContext } from '@uiw/react-tabs-draggable';
171+
import styled from 'styled-components';
172+
173+
const TabItem = styled(Tab)`
97174
background-color: #b9b9b9;
98175
padding: 3px 7px;
99176
border-radius: 5px 5px 0 0;
@@ -123,13 +200,18 @@ function App() {
123200
{ id: 'tab-3-3', children: 'Baidu' },
124201
{ id: 'tab-3-4', children: 'Taobao' },
125202
{ id: 'tab-3-5', children: 'JD' },
203+
{ id: 'tab-3-6', children: 'Apple' },
204+
{ id: 'tab-3-7', children: 'Bing' },
205+
{ id: 'tab-3-8', children: 'Gmail' },
206+
{ id: 'tab-3-9', children: 'Gitter' },
126207
]);
127208
const [test, setTest] = useState(1);
128209
const [activeKey, setActiveKey] = useState('');
129210

130211
const tabClick = (id, evn) => {
131212
evn.stopPropagation();
132213
setActiveKey(id);
214+
setTest(test + 1);
133215
};
134216
const closeHandle = (item, evn) => {
135217
evn.stopPropagation();
@@ -155,10 +237,10 @@ function App() {
155237
>
156238
{data.map((m, idx) => {
157239
return (
158-
<TabIten key={idx} id={m.id} draggable={idx !== 0}>
240+
<TabItem key={idx} id={m.id} draggable={idx !== 0}>
159241
{m.children}
160242
<button onClick={(evn) => closeHandle(m, evn)}>x</button>
161-
</TabIten>
243+
</TabItem>
162244
);
163245
})}
164246
</Tabs>

core/src/Tab.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ export const ItemTypes = {
99
Tab: 'wtabs',
1010
};
1111

12-
export interface TabProps
13-
extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
14-
Pick<TabsProps, 'onTabClick' | 'onTabDrop'> {
12+
export interface TabProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
1513
id: string;
1614
index?: number;
1715
}
@@ -22,8 +20,8 @@ export interface DragItem {
2220
type: string;
2321
}
2422

25-
export const Tab: FC<PropsWithChildren<TabProps>> = ({ children, id, index, onTabClick, onTabDrop, ...props }) => {
26-
const { state, dispatch } = useDataContext();
23+
export const Tab: FC<PropsWithChildren<TabProps>> = ({ children, id, index, ...props }) => {
24+
const { state, onTabClick, onTabDrop, dispatch } = useDataContext();
2725
const ref = useRef<HTMLDivElement>(null);
2826
const [{ handlerId }, drop] = useDrop<DragItem, void, { handlerId: Identifier | null }>({
2927
accept: ItemTypes.Tab,
@@ -80,8 +78,6 @@ export const Tab: FC<PropsWithChildren<TabProps>> = ({ children, id, index, onTa
8078
},
8179
end: (item) => {
8280
onTabDrop && onTabDrop(id, item.index);
83-
// dispatch!({ move: { id, index: item.index }});
84-
// hanlde(id, item.index)
8581
},
8682
collect: (monitor) => {
8783
return {

core/src/Tabs.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import React, { FC, isValidElement, PropsWithChildren, useCallback, useEffect } from 'react';
1+
import React, { FC, isValidElement, PropsWithChildren, useLayoutEffect } from 'react';
22
import { useDrop } from 'react-dnd';
33
import { useDataContext, InitialState } from './store';
44
import { ItemTypes } from './Tab';
55
import { TabsProps } from './';
66

7-
export const Tabs: FC<PropsWithChildren<TabsProps>> = ({ children, onTabClick, onTabDrop, ...props }) => {
7+
export const Tabs: FC<PropsWithChildren<TabsProps>> = ({ children, ...props }) => {
88
const { state, dispatch } = useDataContext();
99
const [, drop] = useDrop(() => ({
1010
accept: ItemTypes.Tab,
1111
}));
12-
const childLength = React.Children.toArray(children).length;
13-
useEffect(() => {
14-
if (childLength !== state.data?.length) {
12+
13+
useLayoutEffect(() => {
14+
if (children) {
1515
const data: InitialState['data'] = [];
1616
React.Children.toArray(children).forEach((item) => {
1717
if (isValidElement(item)) {
@@ -20,7 +20,7 @@ export const Tabs: FC<PropsWithChildren<TabsProps>> = ({ children, onTabClick, o
2020
});
2121
dispatch!({ data });
2222
}
23-
}, [childLength]);
23+
}, [children]);
2424

2525
return (
2626
<div
@@ -33,7 +33,7 @@ export const Tabs: FC<PropsWithChildren<TabsProps>> = ({ children, onTabClick, o
3333
state.data.length > 0 &&
3434
state.data.map(({ element, ...child }, idx) => {
3535
if (isValidElement(element)) {
36-
return React.cloneElement<any>(element, { ...child, onTabClick, onTabDrop, index: idx });
36+
return React.cloneElement<any>(element, { ...child, index: idx });
3737
}
3838
})}
3939
</div>

core/src/hooks.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useLayoutEffect, useMemo, useRef } from 'react';
2+
3+
type Fn<ARGS extends any[], R> = (...args: ARGS) => R;
4+
export const useEventCallback = <A extends any[], R>(fn: Fn<A, R>): Fn<A, R> => {
5+
let ref = useRef<Fn<A, R>>(fn);
6+
useLayoutEffect(() => {
7+
ref.current = fn;
8+
});
9+
return useMemo(
10+
() =>
11+
(...args: A): R => {
12+
const { current } = ref;
13+
return current && current(...args);
14+
},
15+
[],
16+
);
17+
};

core/src/index.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { DndProvider } from 'react-dnd';
2-
import { FC, PropsWithChildren, useEffect } from 'react';
2+
import { FC, PropsWithChildren } from 'react';
33
import { HTML5Backend } from 'react-dnd-html5-backend';
44
import { Tabs } from './Tabs';
55
import { Provider } from './store';
6+
import { useEventCallback } from './hooks';
67

78
export * from './Tab';
89

@@ -15,10 +16,12 @@ export interface TabsProps extends React.DetailedHTMLProps<React.HTMLAttributes<
1516
onTabDrop?: (id: string, index?: number) => void;
1617
}
1718

18-
const Container: FC<PropsWithChildren<TabsProps>> = ({ activeKey, ...props }) => {
19+
const Container: FC<PropsWithChildren<TabsProps>> = ({ activeKey, onTabClick, onTabDrop, ...props }) => {
20+
const tabClick = useEventCallback(onTabClick!);
21+
const tabDrop = useEventCallback(onTabDrop!);
1922
return (
2023
<DndProvider backend={HTML5Backend}>
21-
<Provider init={{ data: [], activeKey, onTabClick: props.onTabClick, onTabDrop: props.onTabDrop }}>
24+
<Provider init={{ data: [], activeKey, onTabClick: tabClick, onTabDrop: tabDrop }}>
2225
<Tabs {...props} />
2326
</Provider>
2427
</DndProvider>

0 commit comments

Comments
 (0)