Skip to content

Commit 5bdcf34

Browse files
fix updateReducer bug
1 parent 29942ac commit 5bdcf34

File tree

5 files changed

+129
-57
lines changed

5 files changed

+129
-57
lines changed

src/useDynamicTabs/index.test.js

Lines changed: 92 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ describe("actions", () => {
5656
);
5757
};
5858
render(<App></App>, container);
59-
_api.select('2');
59+
if (_api.isSelected('2') == false)
60+
_api.select('2');
6061
});
6162
expect(document.querySelector('[tab-id="2"]').className.includes('rc-dyn-tabs-selected')).toBe(true);
63+
expect(op.onChange.mock.calls.length).toBe(1);
6264
expect(op.onSelect.mock.calls.length).toBe(1);
6365
expect(op.onLoad.mock.calls.length).toBe(1);
6466
});
@@ -76,13 +78,15 @@ describe("actions", () => {
7678
);
7779
};
7880
render(<App></App>, container);
79-
_api.open({
80-
id: '3',
81-
title: 'mock tab 3',
82-
closable: true,
83-
panelComponent: <p>tab3 content</p>
84-
});
85-
_api.close('1');
81+
if (_api.isOpen('3') == false)
82+
_api.open({
83+
id: '3',
84+
title: 'mock tab 3',
85+
closable: true,
86+
panelComponent: <p>tab3 content</p>
87+
});
88+
if (_api.isOpen('1'))
89+
_api.close('1');
8690
});
8791
expect(_api.isOpen('3')).toBe(true);
8892
expect(_api.isOpen('1')).toBe(false);
@@ -106,30 +110,33 @@ describe("actions", () => {
106110
);
107111
};
108112
render(<App></App>, container);
109-
_api.setTab('1', { closable: false });
110-
_api.setOption('direction', 'rtl');
111-
_api.setOption('tabComponent', function (props) {
112-
return (
113-
<a {...props.tabProps}>
114-
{props.children}
115-
{
116-
props.hasOwnProperty('iconProps') &&
117-
<span {...props.iconProps}></span>
118-
}
119-
</a>
120-
);
121-
});
122-
_api.on('onInit', function () {
123-
if (!counter) {
124-
counter++;
125-
this.setTab('1', {
126-
panelComponent: function (props) {
127-
return <div id="updatedPanel1"></div>;
128-
}
129-
}).refresh();
130-
}
131-
});
132-
_api.refresh();
113+
if (_api.getTab('1').closable == true)
114+
_api.setTab('1', { closable: false });
115+
if (_api.getOption('direction') === 'ltr') {
116+
_api.setOption('direction', 'rtl');
117+
_api.setOption('tabComponent', function (props) {
118+
return (
119+
<a {...props.tabProps}>
120+
{props.children}
121+
{
122+
props.hasOwnProperty('iconProps') &&
123+
<span {...props.iconProps}></span>
124+
}
125+
</a>
126+
);
127+
});
128+
_api.on('onInit', function () {
129+
if (!counter) {
130+
counter++;
131+
this.setTab('1', {
132+
panelComponent: function (props) {
133+
return <div id="updatedPanel1"></div>;
134+
}
135+
}).refresh();
136+
}
137+
});
138+
_api.refresh();
139+
}
133140
});
134141
expect(document.getElementById('updatedPanel1') != null).toBe(true);
135142
expect(document.querySelector('li[tab-id="1"] .rc-dyn-tabs-close') == null).toBe(true);
@@ -139,6 +146,47 @@ describe("actions", () => {
139146
expect(op.onInit.mock.calls.length).toBe(3);
140147
});
141148
});
149+
describe("calling some action inside the events options", () => {
150+
let _api;
151+
test("calling select method inside the onLoad option", () => {
152+
const op = {
153+
tabs: [{
154+
id: '1',
155+
title: 'mock tab 1',
156+
closable: true,
157+
panelComponent: <p>tab1 content</p>
158+
}, {
159+
id: '2',
160+
title: 'mock tab 2',
161+
iconClass: 'ui-icon ui-icon-seek-end',
162+
closable: false,
163+
panelComponent: <p>tab2 content</p>
164+
}],
165+
selectedTabID: '1',
166+
onLoad: jest.fn(function () { this.select('2'); }),
167+
onSelect: jest.fn(function () { }),
168+
onChange: jest.fn(function () { }),
169+
onInit: jest.fn(function () { })
170+
};
171+
act(() => {
172+
const App = function (props) {
173+
const [Tablist, Panellist, api] = useDynTabs(op);
174+
_api = api;
175+
return (
176+
<div>
177+
<Tablist></Tablist>
178+
<Panellist></Panellist>
179+
</div>
180+
);
181+
};
182+
render(<App></App>, container);
183+
});
184+
expect(op.onLoad.mock.calls.length === 1).toBe(true);
185+
expect(op.onInit.mock.calls.length === 2).toBe(true);
186+
expect(op.onChange.mock.calls.length === 1).toBe(true);
187+
expect(op.onSelect.mock.calls.length === 1).toBe(true);
188+
});
189+
});
142190
describe("events", () => {
143191
test('checking events execution count and their parameters ', () => {
144192
let _api, contextProps = '';
@@ -183,14 +231,16 @@ describe("events", () => {
183231
);
184232
};
185233
render(<App></App>, container);
186-
_api.on('onSelect', onSelectHandler);
187-
_api.open({
188-
id: '3',
189-
title: 'mock tab 3',
190-
closable: true,
191-
panelComponent: <p>tab3 content</p>
192-
});
193-
_api.close('1');
234+
_api.one('onSelect', onSelectHandler);
235+
if (_api.isOpen('3') == false)
236+
_api.open({
237+
id: '3',
238+
title: 'mock tab 3',
239+
closable: true,
240+
panelComponent: <p>tab3 content</p>
241+
});
242+
if (_api.isSelected('2') == false)
243+
_api.close('1');
194244
});
195245
//onload
196246
expect(op.onLoad.mock.calls.length).toBe(1);
@@ -201,6 +251,7 @@ describe("events", () => {
201251
expect(Object.prototype.toString.call(op.onChange.mock.calls[0][0]) === '[object Object]').toBe(true);
202252
expect(op.onChange.mock.calls[0][0].hasOwnProperty('currentData')).toBe(true);
203253
expect(op.onChange.mock.calls[0][0].hasOwnProperty('perviousData')).toBe(true);
254+
204255
//onSelect
205256
expect(onSelectHandler.mock.calls.length).toBe(1);
206257
expect(op.onSelect.mock.calls.length).toBe(1);
@@ -210,6 +261,7 @@ describe("events", () => {
210261
expect(onSelectHandler.mock.calls[0][0].hasOwnProperty('currentSelectedTabId')).toBe(true);
211262
expect(op.onSelect.mock.calls[0][0].hasOwnProperty('perviousSelectedTabId')).toBe(true);
212263
expect(onSelectHandler.mock.calls[0][0].hasOwnProperty('perviousSelectedTabId')).toBe(true);
264+
213265
//onclose
214266
expect(op.onClose.mock.calls.length).toBe(1);
215267
expect(op.onClose.mock.calls[0][0].constructor === Array && op.onClose.mock.calls[0][0][0] === '1').toBe(true);

src/useDynamicTabs/useDynamicTabs.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ function useDynamicTabs(options) {
1212
const { current: { api } } = ref
1313
, _ref = ref.current
1414
, [state, dispatch] = useReducer(reducer, api.getInitialState());
15-
api.updateReducer(state, dispatch);
15+
api.updateStateRef(state).updateDispatch(dispatch).setPerviousState(state);
1616
useLayoutEffect(() => {
1717
api.trigger('onLoad', api.userProxy);
1818
return () => { api.trigger('onDestroy', api.userProxy); };
1919
}, []);
20+
useLayoutEffect(() => {
21+
api.updatePerviousState(state);
22+
}, [state]);
2023
useLayoutEffect(() => {
2124
api.trigger('onInit', api.userProxy);
2225
});

src/utils/api/api.factory.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ const _apiProps = {
4141
getOption: function (name) { return this.optionsManager.getOption(name); },
4242
setOption: function (name, value) { return this.optionsManager.setOption(name, value); },
4343
getCopyPerviousData: function () { return this.helper.getCopyState(this._perviousState); },
44-
getCopyData: function () { return this.helper.getCopyState(this._state); },
45-
isSelected: function (id = missingParamEr('isSelected')) { return this._state.selectedTabID == id; },
46-
isOpen: function (id = missingParamEr('isOpen')) { return this._state.openTabIDs.indexOf(id) >= 0; },
44+
getCopyData: function () { return this.helper.getCopyState(this.stateRef); },
45+
isSelected: function (id = missingParamEr('isSelected')) { return this.stateRef.selectedTabID == id; },
46+
isOpen: function (id = missingParamEr('isOpen')) { return this.stateRef.openTabIDs.indexOf(id) >= 0; },
4747
_getOnChangePromise: function () {
4848
return new (Promise)(resolve => { this.one('onChange', () => { resolve.call(this.userProxy); }); });
4949
},
@@ -61,11 +61,11 @@ const _apiProps = {
6161
return _findOpenedAndNoneDisableTabId.call(this, [...this.activedTabsHistory.tabsId], true);
6262
}
6363
, _getPreSiblingTabId = function () {
64-
const data = this._state, arr = data.openTabIDs;
64+
const data = this.stateRef, arr = data.openTabIDs;
6565
return _findOpenedAndNoneDisableTabId.call(this, arr.slice(0, arr.indexOf(data.selectedTabID)), true);
6666
}
6767
, _getNextSiblingTabId = function () {
68-
const data = this._state, arr = data.openTabIDs;
68+
const data = this.stateRef, arr = data.openTabIDs;
6969
return _findOpenedAndNoneDisableTabId.call(this, arr.slice(arr.indexOf(data.selectedTabID) + 1));
7070
};
7171
return function () {
@@ -94,7 +94,7 @@ const _apiProps = {
9494
},
9595
close: function (id = missingParamEr('close')) {
9696
if (this.isSelected(id)) {
97-
const _openTabsId = [...this._state.openTabIDs];
97+
const _openTabsId = [...this.stateRef.openTabIDs];
9898
_openTabsId.splice(_openTabsId.indexOf(id), 1);
9999
this.select(this._findTabIdForSwitching());
100100
return this.__close(id);

src/utils/api/api.test.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@ beforeEach(() => {
55
});
66
describe('user api : ', () => {
77
test('list all available props for consumer', () => {
8-
const userApiProps = `getTab,setTab,off,on,one,getOption,setOption,getCopyPerviousData,getCopyData,isSelected,isOpen,select,open,close,refresh`;
9-
expect(Object.keys(obj.userProxy).join() === userApiProps).toBe(true);
8+
const userApi = ['getTab', 'setTab', 'off', 'on', 'one', 'getOption', 'setOption', 'getCopyPerviousData', 'getCopyData',
9+
'isSelected', 'isOpen', 'select', 'open', 'close', 'refresh'];
10+
expect(Object.keys(obj.userProxy).length === userApi.length).toBe(true);
11+
let _isEqual = true;
12+
userApi.map((value) => {
13+
if (!obj.userProxy.hasOwnProperty(value)) {
14+
_isEqual = false;
15+
}
16+
});
17+
expect(_isEqual).toBe(true);
1018
});
1119
});
1220

src/utils/api/baseApi.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import actions from '../stateManagement/actions';
22
import Helper from '../helper.js';
33
function BaseApi(helper) {
44
this._helper = helper;
5-
this._state = {};
5+
this._state = null;// it will be updated after execution of useState
66
this._initialState = null;
7-
this._perviousState = {};
8-
this._dispatch = () => { };
7+
this._perviousState = null;// it is a pervious value of this._state
8+
this._dispatch = null;
99
helper.setNoneEnumProps(this, {
1010
forceUpdateState: {},
11-
stateRef: {}
11+
stateRef: {}// both of stateRef and state have a same reference. It will be updated in each execution of useDynamicTabs.js
1212
});
1313
}
1414
BaseApi.prototype._select = function (tabId) { this._dispatch({ type: actions.active, tabId }); };
@@ -19,13 +19,22 @@ BaseApi.prototype._refresh = function () {
1919
this._dispatch({ type: actions.refresh });
2020
};
2121
Helper.setNoneEnumProps(BaseApi.prototype, {
22-
updateReducer: function (state, dispatch) {
22+
updateStateRef: function (state) {
2323
this.stateRef = state;
24+
return this;
25+
},
26+
updateDispatch: function (dispatch) { this._dispatch = dispatch; return this; },
27+
setPerviousState: function (state) {
28+
if (this._perviousState == null) {
29+
this._perviousState = this._helper.getCopyState(state);
30+
this._state = this._helper.getCopyState(state);
31+
}
32+
return this;
33+
},
34+
updatePerviousState: function (state) {
2435
this._perviousState = this._helper.getCopyState(this._state);
2536
this._state = this._helper.getCopyState(state);
26-
this._perviousState = this._perviousState.hasOwnProperty('openTabIDs') ? this._perviousState :
27-
this._helper.getCopyState(this._state);
28-
this._dispatch = dispatch;
37+
return this;
2938
}
3039
});
3140
export default BaseApi;

0 commit comments

Comments
 (0)