Skip to content

Commit 908612e

Browse files
Fix collapse rows after changing data (#241)
* Keep state of nodes for opened * Use global hook * Fix test issue Co-authored-by: EconomistBot <52030965+EconomistBot@users.noreply.github.com>
1 parent 014f778 commit 908612e

File tree

5 files changed

+329
-286
lines changed

5 files changed

+329
-286
lines changed

package.json

Lines changed: 98 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,103 @@
11
{
2-
"name": "react-native-nested-listview",
3-
"version": "0.8.0",
4-
"description": "Nested Listview for React native",
5-
"main": "./dist/index.js",
6-
"types": "./dist/index.d.ts",
7-
"scripts": {
8-
"test": "jest",
9-
"test-coverage": "jest && codecov",
10-
"tslint": "tslint -p .",
11-
"type-check": "tsc --noEmit",
12-
"prepublishOnly": "tsc -p ./ --outDir dist/",
13-
"add-react": "yarn add react@16.9.0 && yarn add react-native@0.60.5",
14-
"remove-react": "yarn remove react && yarn remove react-native",
15-
"prettier-check": "prettier --check 'src/**/*.{ts, tsx}'",
16-
"prettier-format": "prettier --write 'src/**/*.{ts, tsx}'"
17-
},
18-
"repository": {
19-
"type": "git",
20-
"url": "git+https://github.com/fjmorant/react-native-nested-listview.git"
21-
},
22-
"dependencies": {
23-
"react-fast-compare": "2.0.4",
24-
"shortid": "2.2.15"
25-
},
26-
"peerDependencies": {
27-
"react": "*",
28-
"react-native": "*"
29-
},
30-
"devDependencies": {
31-
"@babel/core": "7.7.2",
32-
"@babel/runtime": "7.7.2",
33-
"@types/jest": "24.0.23",
34-
"@types/react": "16.9.11",
35-
"@types/react-native": "0.60.22",
36-
"@types/react-test-renderer": "16.9.1",
37-
"@types/shortid": "0.0.29",
38-
"babel-jest": "24.9.0",
39-
"codecov": "3.6.1",
40-
"istanbul": "0.4.5",
41-
"istanbul-api": "2.1.6",
42-
"istanbul-reports": "2.2.6",
43-
"jest": "24.9.0",
44-
"metro-react-native-babel-preset": "0.57.0",
45-
"prettier": "1.19.1",
46-
"prettier-eslint": "9.0.1",
47-
"react": "16.9.0",
48-
"react-native": "0.61.4",
49-
"react-test-renderer": "16.12.0",
50-
"ts-jest": "24.1.0",
51-
"tslint": "5.20.1",
52-
"typescript": "3.7.2"
53-
},
54-
"keywords": [
55-
"react",
56-
"native",
57-
"list",
58-
"nested"
59-
],
60-
"author": "Javier Morant",
61-
"license": "MIT",
62-
"bugs": {
63-
"url": "https://github.com/fjmorant/react-native-nested-listview/issues"
64-
},
65-
"homepage": "https://github.com/fjmorant/react-native-nested-listview#readme",
66-
"jest": {
67-
"preset": "react-native",
68-
"moduleFileExtensions": [
69-
"ts",
70-
"tsx",
71-
"js"
72-
],
73-
"transform": {
74-
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
75-
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
2+
"name": "react-native-nested-listview",
3+
"version": "0.8.0",
4+
"description": "Nested Listview for React native",
5+
"main": "./dist/index.js",
6+
"types": "./dist/index.d.ts",
7+
"scripts": {
8+
"test": "jest",
9+
"test-coverage": "jest && codecov",
10+
"tslint": "tslint -p .",
11+
"type-check": "tsc --noEmit",
12+
"prepublishOnly": "tsc -p ./ --outDir dist/",
13+
"add-react": "yarn add react@16.9.0 && yarn add react-native@0.61.4",
14+
"remove-react": "yarn remove react && yarn remove react-native",
15+
"prettier-check": "prettier --check 'src/**/*.{ts, tsx}'",
16+
"prettier-format": "prettier --write 'src/**/*.{ts, tsx}'"
7617
},
77-
"transformIgnorePatterns": [
78-
"node_modules/(?!(jest-)|react-native|react-navigation|react-clone-referenced-element|mobx-react)"
79-
],
80-
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
81-
"modulePathIgnorePatterns": [
82-
"<rootDir>/examples/"
83-
],
84-
"testPathIgnorePatterns": [
85-
"\\.snap$",
86-
"<rootDir>/node_modules/",
87-
"<rootDir>/examples/",
88-
"<rootDir>/lib/",
89-
"<rootDir>/dist/"
18+
"repository": {
19+
"type": "git",
20+
"url": "git+https://github.com/fjmorant/react-native-nested-listview.git"
21+
},
22+
"dependencies": {
23+
"object-hash": "2.0.1",
24+
"react-fast-compare": "2.0.4",
25+
"shortid": "2.2.15",
26+
"use-global-hook": "0.1.12"
27+
},
28+
"peerDependencies": {
29+
"react": "*",
30+
"react-native": "*"
31+
},
32+
"devDependencies": {
33+
"@babel/core": "7.7.2",
34+
"@babel/runtime": "7.7.2",
35+
"@types/jest": "24.0.23",
36+
"@types/use-global-hook": "0.1.2",
37+
"@types/object-hash": "1.3.1",
38+
"@types/react": "16.9.11",
39+
"@types/react-native": "0.60.22",
40+
"@types/react-test-renderer": "16.9.1",
41+
"@types/shortid": "0.0.29",
42+
"babel-jest": "24.9.0",
43+
"codecov": "3.6.1",
44+
"istanbul": "0.4.5",
45+
"istanbul-api": "2.1.6",
46+
"istanbul-reports": "2.2.6",
47+
"jest": "24.9.0",
48+
"metro-react-native-babel-preset": "0.57.0",
49+
"prettier": "1.19.1",
50+
"prettier-eslint": "9.0.1",
51+
"react": "16.9.0",
52+
"react-native": "0.61.4",
53+
"react-test-renderer": "16.12.0",
54+
"ts-jest": "24.1.0",
55+
"tslint": "5.20.1",
56+
"typescript": "3.7.2"
57+
},
58+
"keywords": [
59+
"react",
60+
"native",
61+
"list",
62+
"nested"
9063
],
91-
"coverageDirectory": "./coverage/",
92-
"collectCoverage": true,
93-
"globals": {
94-
"ts-jest": {
95-
"babelConfig": true
96-
}
64+
"author": "Javier Morant",
65+
"license": "MIT",
66+
"bugs": {
67+
"url": "https://github.com/fjmorant/react-native-nested-listview/issues"
68+
},
69+
"homepage": "https://github.com/fjmorant/react-native-nested-listview#readme",
70+
"jest": {
71+
"preset": "react-native",
72+
"moduleFileExtensions": [
73+
"ts",
74+
"tsx",
75+
"js"
76+
],
77+
"transform": {
78+
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
79+
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
80+
},
81+
"transformIgnorePatterns": [
82+
"node_modules/(?!(jest-)|react-native|react-navigation|react-clone-referenced-element|mobx-react|use-global-hook)"
83+
],
84+
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
85+
"modulePathIgnorePatterns": [
86+
"<rootDir>/examples/"
87+
],
88+
"testPathIgnorePatterns": [
89+
"\\.snap$",
90+
"<rootDir>/node_modules/",
91+
"<rootDir>/examples/",
92+
"<rootDir>/lib/",
93+
"<rootDir>/dist/"
94+
],
95+
"coverageDirectory": "./coverage/",
96+
"collectCoverage": true,
97+
"globals": {
98+
"ts-jest": {
99+
"babelConfig": true
100+
}
101+
}
97102
}
98-
}
99103
}

src/NestedListView.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
/* @flow */
2-
1+
import hashObjectGenerator from 'object-hash'
32
import React, {useEffect, useState} from 'react'
43
import isEqual from 'react-fast-compare'
54
import {StyleSheet, Text, View} from 'react-native'
6-
import * as shortid from 'shortid'
5+
import shortid from 'shortid'
76
import NodeView, {INode} from './NodeView'
87

98
const styles = StyleSheet.create({
@@ -28,15 +27,23 @@ export interface IProps {
2827
renderNode: (elem: INode, level?: number) => any
2928
onNodePressed?: (node?: INode) => void
3029
getChildrenName: (elem: any) => any
31-
style?: any
30+
style?: StyleSheet
31+
keepOpenedState?: boolean
3232
}
3333

3434
export interface IState {
35-
root: any
35+
root: INode
3636
}
3737

3838
const NestedListView = React.memo(
39-
({getChildrenName, renderNode, data, onNodePressed, extraData}: IProps) => {
39+
({
40+
getChildrenName,
41+
renderNode,
42+
data,
43+
onNodePressed,
44+
extraData,
45+
keepOpenedState,
46+
}: IProps) => {
4047
const generateIds = (node?: INode) => {
4148
if (!node) {
4249
return {
@@ -57,15 +64,21 @@ const NestedListView = React.memo(
5764
generateIds(children[index])
5865
)
5966
}
67+
if (node._internalId) {
68+
delete node._internalId
69+
}
6070

61-
node._internalId = shortid.generate()
71+
node._internalId = hashObjectGenerator(node, {
72+
algorithm: 'md5',
73+
encoding: 'base64',
74+
})
6275

6376
return node
6477
}
6578

6679
const generateRootNode = (props: IProps): INode => {
6780
return {
68-
_internalId: shortid.generate(),
81+
_internalId: 'root',
6982
items: props.data
7083
? props.data.map((_: INode, index: number) =>
7184
generateIds(props.data[index])
@@ -133,6 +146,7 @@ const NestedListView = React.memo(
133146
level={0}
134147
renderNode={renderNode}
135148
extraData={extraData}
149+
keepOpenedState={keepOpenedState}
136150
/>
137151
)
138152
},

src/NodeView.tsx

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
/* @flow */
2-
31
import React, {useEffect, useState} from 'react'
42
import isEqual from 'react-fast-compare'
53
import {FlatList, TouchableWithoutFeedback, View} from 'react-native'
4+
import globalHook from 'use-global-hook'
65

76
export interface INode {
87
_internalId: string
@@ -21,6 +20,7 @@ export interface IProps {
2120
renderNode: (item: any, level: number) => any
2221
renderChildrenNode?: (item: any) => any
2322
extraData?: any
23+
keepOpenedState?: boolean
2424
}
2525

2626
export interface IState {
@@ -29,6 +29,22 @@ export interface IState {
2929
opened: boolean
3030
}
3131

32+
const actions = {
33+
setOpenedNode: (store: any, {internalId, opened}: any) => {
34+
store.setState({
35+
nodesState: {...store.state.nodesState, [internalId]: opened},
36+
})
37+
},
38+
}
39+
40+
const useGlobal = globalHook(
41+
React,
42+
{
43+
nodesState: {root: true},
44+
},
45+
actions
46+
)
47+
3248
const NodeView = React.memo(
3349
({
3450
renderNode,
@@ -37,11 +53,17 @@ const NodeView = React.memo(
3753
getChildrenName,
3854
node,
3955
onNodePressed,
56+
keepOpenedState,
4057
}: IProps) => {
58+
const [globalState, globalActions]: [any, any] = useGlobal()
59+
4160
// tslint:disable-next-line:variable-name
4261
const [_node, setNode]: [INode, any] = useState({
43-
opened: false,
4462
...node,
63+
opened:
64+
keepOpenedState && globalState.nodesState[node._internalId]
65+
? globalState.nodesState[node._internalId]
66+
: node.opened,
4567
})
4668

4769
useEffect(() => {
@@ -53,6 +75,13 @@ const NodeView = React.memo(
5375

5476
// tslint:disable-next-line:variable-name
5577
const _onNodePressed = () => {
78+
if (keepOpenedState) {
79+
globalActions.setOpenedNode({
80+
internalId: _node._internalId,
81+
opened: !_node.opened,
82+
})
83+
}
84+
5685
setNode({
5786
..._node,
5887
opened: !_node.opened,
@@ -73,6 +102,7 @@ const NodeView = React.memo(
73102
extraData={extraData}
74103
onNodePressed={onNodePressed}
75104
renderNode={renderNode}
105+
keepOpenedState={keepOpenedState}
76106
/>
77107
)
78108
}
@@ -83,14 +113,18 @@ const NodeView = React.memo(
83113
const rootChildrenName = getChildrenName(_node)
84114
const rootChildren = _node[rootChildrenName]
85115

116+
const isNodeOpened =
117+
(keepOpenedState && globalState.nodesState[node._internalId]) ||
118+
_node.opened
119+
86120
return (
87121
<>
88122
{!_node.hidden ? (
89123
<TouchableWithoutFeedback onPress={_onNodePressed}>
90124
<View>{renderNode(_node, level)}</View>
91125
</TouchableWithoutFeedback>
92126
) : null}
93-
{_node.opened && rootChildren ? (
127+
{isNodeOpened && rootChildren ? (
94128
<FlatList
95129
data={rootChildren}
96130
renderItem={renderItem}

0 commit comments

Comments
 (0)