@applicaster/zapp-react-native-ui-components 13.0.0-alpha.7192404241 → 13.0.0-alpha.7196055925

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/Components/BaseFocusable/index.ios.ts +1 -1
  2. package/Components/BaseFocusable/index.tsx +1 -1
  3. package/Components/Focusable/Touchable.tsx +19 -20
  4. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +66 -0
  5. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -1
  6. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -11
  7. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/__tests__/index.test.ts +93 -0
  8. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +1 -1
  9. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  10. package/Components/MasterCell/utils/index.ts +1 -1
  11. package/Components/ModalComponent/Header/index.tsx +3 -3
  12. package/Components/River/ComponentsMap/ComponentsMap.tsx +39 -65
  13. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +78 -51
  14. package/Components/River/RiverFooter.tsx +39 -9
  15. package/Components/River/RiverItem.tsx +37 -2
  16. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +148 -31
  17. package/Components/River/__tests__/componentsMap.test.js +3 -4
  18. package/Components/Screen/hooks.ts +56 -0
  19. package/Components/Screen/index.tsx +13 -39
  20. package/Components/Tabs/Tab.tsx +6 -6
  21. package/Components/TextInputTv/index.tsx +2 -2
  22. package/Components/Transitioner/AnimationManager.js +8 -8
  23. package/Components/Transitioner/Scene.tsx +52 -23
  24. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +59 -43
  25. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -2
  26. package/Components/Transitioner/index.js +8 -4
  27. package/Components/VideoLive/LiveImageManager.ts +27 -1
  28. package/Components/VideoLive/PlayerLiveImageComponent.tsx +29 -21
  29. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +51 -1
  30. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
  31. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +7 -6
  32. package/Components/VideoModal/ModalAnimation/utils.ts +2 -2
  33. package/Contexts/ScreenContext/index.tsx +3 -2
  34. package/Decorators/ZappPipesDataConnector/__tests__/Hero.js +1 -1
  35. package/package.json +5 -5
@@ -25,7 +25,7 @@ type Props = {
25
25
  };
26
26
 
27
27
  export class BaseFocusable<
28
- T extends Props = Props
28
+ T extends Props = Props,
29
29
  > extends BaseFocusableInterface<T> {
30
30
  constructor(props) {
31
31
  super(props);
@@ -35,7 +35,7 @@ type Props = {
35
35
  };
36
36
 
37
37
  export class BaseFocusable<
38
- T extends Props = Props
38
+ T extends Props = Props,
39
39
  > extends BaseFocusableInterface<T> {
40
40
  constructor(props) {
41
41
  super(props);
@@ -1,25 +1,24 @@
1
1
  import * as React from "react";
2
2
 
3
- type Props =
4
- | {
5
- id: string;
6
- children: React.ReactNode;
7
- onPress?: (ref: FocusManager.TouchableRef) => void;
8
- onPressIn?: (ref: FocusManager.TouchableRef) => void;
9
- onPressOut?: (ref: FocusManager.TouchableRef) => void;
10
- onLongPress?: (ref: FocusManager.TouchableRef) => void;
11
- onFocus?: (
12
- ref: FocusManager.TouchableRef,
13
- options: FocusManager.Android.CallbackOptions
14
- ) => void;
15
- onBlur?: (
16
- ref: FocusManager.TouchableRef,
17
- options: FocusManager.Android.CallbackOptions
18
- ) => void;
19
-
20
- disableFocus?: boolean;
21
- blockFocus?: boolean;
22
- } & Partial<ParentFocus>;
3
+ type Props = {
4
+ id: string;
5
+ children: React.ReactNode;
6
+ onPress?: (ref: FocusManager.TouchableRef) => void;
7
+ onPressIn?: (ref: FocusManager.TouchableRef) => void;
8
+ onPressOut?: (ref: FocusManager.TouchableRef) => void;
9
+ onLongPress?: (ref: FocusManager.TouchableRef) => void;
10
+ onFocus?: (
11
+ ref: FocusManager.TouchableRef,
12
+ options: FocusManager.Android.CallbackOptions
13
+ ) => void;
14
+ onBlur?: (
15
+ ref: FocusManager.TouchableRef,
16
+ options: FocusManager.Android.CallbackOptions
17
+ ) => void;
18
+
19
+ disableFocus?: boolean;
20
+ blockFocus?: boolean;
21
+ } & Partial<ParentFocus>;
23
22
 
24
23
  export class Touchable extends React.Component<Props> {
25
24
  onPress(focusableRef: FocusManager.TouchableRef): void {
@@ -0,0 +1,66 @@
1
+ import {
2
+ BorderContainerView,
3
+ getBorderPadding, // Export for testing (using a double underscore prefix is a common convention)
4
+ } from "../index";
5
+ import * as React from "react";
6
+ import { render } from "@testing-library/react-native";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
+ import { View } from "react-native";
9
+
10
+ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
11
+ toNumberWithDefaultZero: jest.fn((value) => Number(value) || 0),
12
+ }));
13
+
14
+ describe("BorderContainerView", () => {
15
+ describe("getBorderPadding", () => {
16
+ it("returns 0 for inside", () => {
17
+ expect(getBorderPadding("inside", 10)).toBe(0);
18
+ });
19
+
20
+ it("returns borderWidth / 2 for center", () => {
21
+ expect(getBorderPadding("center", 10)).toBe(5);
22
+ });
23
+
24
+ it("returns borderWidth for outside", () => {
25
+ expect(getBorderPadding("outside", 10)).toBe(10);
26
+ });
27
+
28
+ it("returns borderWidth for invalid position", () => {
29
+ // @ts-ignore
30
+ expect(getBorderPadding("other_value", 10)).toBe(10);
31
+ });
32
+ });
33
+
34
+ it("Border component renders null if no borderPosition", () => {
35
+ const style = { borderWidth: 5, borderRadius: 10, borderColor: "red" };
36
+
37
+ const padding = {
38
+ paddingTop: 2,
39
+ paddingRight: 3,
40
+ paddingBottom: 4,
41
+ paddingLeft: 5,
42
+ };
43
+
44
+ const borderPosition = null;
45
+
46
+ const { queryByTestId } = render(
47
+ <BorderContainerView
48
+ style={style}
49
+ testID="border-container"
50
+ borderPosition={borderPosition}
51
+ borderPaddingTop={toNumberWithDefaultZero(padding.paddingTop)}
52
+ borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
53
+ borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
54
+ borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
55
+ >
56
+ <View testID="child" />
57
+ </BorderContainerView>
58
+ );
59
+
60
+ const children = queryByTestId("border-container").children;
61
+
62
+ expect(children[1].props.testID).toBe("child");
63
+
64
+ expect(children[0].children.length).toBe(0);
65
+ });
66
+ });
@@ -28,7 +28,10 @@ const styles = StyleSheet.create({
28
28
  },
29
29
  });
30
30
 
31
- const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
31
+ export const getBorderPadding = (
32
+ borderPosition: BorderPosition,
33
+ borderWidth: number
34
+ ) => {
32
35
  switch (borderPosition) {
33
36
  case "inside":
34
37
  return 0;
@@ -38,22 +38,16 @@ function Image({
38
38
 
39
39
  const shouldRenderImg = source && !error;
40
40
 
41
- // Default placeholder image fallback is empty string because it won't rerender
42
- // in case of undefined or {uri: undefined} source
43
- // https://github.com/facebook/react-native/issues/9195
44
- const defaultPlaceHolderImage = React.useMemo(
45
- () => ({ uri: placeholderImage || "" }),
46
- [source, error]
47
- );
41
+ const _source = shouldRenderImg
42
+ ? withDimensions(source)
43
+ : { uri: placeholderImage };
48
44
 
49
45
  return (
50
- // @ts-ignore
51
46
  <MemoizedImage
52
47
  style={style as ImageStyle}
53
48
  onError={React.useCallback(() => setErrorState(true), [])}
54
- source={
55
- shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
56
- }
49
+ // as we have defaults as "" for placeholder image, we need to pass undefined to source to not throw warnings
50
+ source={_source?.uri ? _source : undefined}
57
51
  {...R.omit(["source"], otherProps)}
58
52
  />
59
53
  );
@@ -0,0 +1,93 @@
1
+ import { ImageBorderContainer } from "../index";
2
+ import { getImageContainerMarginStyles } from "@applicaster/zapp-react-native-utils/cellUtils";
3
+
4
+ const mockValue = (key) => {
5
+ const values = {
6
+ cell_padding_top: 1,
7
+ cell_padding_left: 2,
8
+ cell_padding_right: 3,
9
+ cell_padding_bottom: 4,
10
+ image_border_size: 5,
11
+ image_focused_border_color: "red",
12
+ image_selected_border_color: "blue",
13
+ image_focused_selected_border_color: "green",
14
+ image_border_color: "black",
15
+ image_corner_radius: 10,
16
+ image_border_position: "outside",
17
+ image_border_padding_top: 6,
18
+ image_border_padding_right: 7,
19
+ image_border_padding_bottom: 8,
20
+ image_border_padding_left: 9,
21
+ image_margin_top: 11,
22
+ image_margin_left: 12,
23
+ image_margin_right: 13,
24
+ image_margin_bottom: 14,
25
+ };
26
+
27
+ return values[key] || 0;
28
+ };
29
+
30
+ describe("ImageBorderContainer", () => {
31
+ it("calculates style properties correctly", () => {
32
+ const marginStyles = getImageContainerMarginStyles({ value: mockValue });
33
+
34
+ expect(marginStyles.marginTop).toBe(11);
35
+ expect(marginStyles.marginLeft).toBe(12);
36
+ expect(marginStyles.marginRight).toBe(13);
37
+ expect(marginStyles.marginBottom).toBe(14);
38
+
39
+ const props = {
40
+ value: mockValue,
41
+ state: "default",
42
+ imageStyles: {},
43
+ };
44
+
45
+ const result = ImageBorderContainer(props);
46
+
47
+ expect(result.style.marginTop).toBe(12); // 11 + 1
48
+ expect(result.style.marginLeft).toBe(14); // 12 + 2
49
+ expect(result.style.marginRight).toBe(16); // 13 + 3
50
+ expect(result.style.marginBottom).toBe(18); // 14 + 4
51
+ expect(result.style.borderWidth).toBe(5);
52
+ expect(result.style.borderColor).toBe("black");
53
+ expect(result.style.borderRadius).toBe(10);
54
+ expect(result.additionalProps.borderPosition).toBe("outside");
55
+ expect(result.additionalProps.borderPaddingTop).toBe(6);
56
+ expect(result.additionalProps.borderPaddingRight).toBe(7);
57
+ expect(result.additionalProps.borderPaddingBottom).toBe(8);
58
+ expect(result.additionalProps.borderPaddingLeft).toBe(9);
59
+ });
60
+
61
+ it("handles focused state correctly", () => {
62
+ const props = {
63
+ value: mockValue,
64
+ state: "focused",
65
+ imageStyles: {},
66
+ };
67
+
68
+ const result = ImageBorderContainer(props);
69
+ expect(result.style.borderColor).toBe("red");
70
+ });
71
+
72
+ it("handles selected state correctly", () => {
73
+ const props = {
74
+ value: mockValue,
75
+ state: "selected",
76
+ imageStyles: {},
77
+ };
78
+
79
+ const result = ImageBorderContainer(props);
80
+ expect(result.style.borderColor).toBe("blue");
81
+ });
82
+
83
+ it("handles focused and selected state correctly", () => {
84
+ const props = {
85
+ value: mockValue,
86
+ state: "focused_selected",
87
+ imageStyles: {},
88
+ };
89
+
90
+ const result = ImageBorderContainer(props);
91
+ expect(result.style.borderColor).toBe("green");
92
+ });
93
+ });
@@ -17,7 +17,7 @@ const SECONDARY_IMAGE_PREFIX = "secondary_image";
17
17
 
18
18
  type ImageSizing = typeof IMAGE_SIZING_FIT | typeof IMAGE_SIZING_FILL;
19
19
 
20
- type ImagePosition = typeof IMAGE_POSITION[keyof typeof IMAGE_POSITION];
20
+ type ImagePosition = (typeof IMAGE_POSITION)[keyof typeof IMAGE_POSITION];
21
21
 
22
22
  export type DisplayMode =
23
23
  | typeof DISPLAY_MODE_FIXED
@@ -39,6 +39,6 @@ describe("image with no source", () => {
39
39
  );
40
40
 
41
41
  const testInstance = testRenderer.root;
42
- expect(testInstance.findByType(Image).props.source).toEqual({ uri: "" });
42
+ expect(testInstance.findByType(Image).props.source).toBeUndefined();
43
43
  });
44
44
  });
@@ -103,7 +103,7 @@ export const useFilterChildren = <
103
103
  item: any;
104
104
  pluginIdentifier: string;
105
105
  };
106
- }
106
+ },
107
107
  >(
108
108
  children: T[]
109
109
  ): T[] => {
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo } from "react";
2
- import { View, StyleSheet, LayoutChangeEvent } from "react-native";
2
+ import { LayoutChangeEvent, StyleSheet, View } from "react-native";
3
3
  import { Title, TitleProps } from "./Title";
4
4
  import { CloseButton, CloseButtonProps } from "./CloseButton";
5
5
  import { getCloseButtonProps, getTitleProps } from "./utils";
@@ -60,8 +60,8 @@ export function ModalHeader(props: Props) {
60
60
  width: width - buttonsContainerWidth,
61
61
  }}
62
62
  >
63
- {title && <Title {...titleProps} />}
64
- {summary && <Title {...summaryProps} />}
63
+ {title ? <Title {...titleProps} /> : null}
64
+ {summary ? <Title {...summaryProps} /> : null}
65
65
  </View>
66
66
  <View style={{ width: buttonsContainerWidth }}>
67
67
  <CloseButton {...closeButtonProps} />
@@ -70,21 +70,6 @@ function ComponentsMapComponent(props: Props) {
70
70
 
71
71
  const [flatListHeight, setFlatListHeight] = React.useState(null);
72
72
 
73
- const {
74
- isAnyLoading,
75
- isAllLoaded,
76
- waitForAllComponents,
77
- onLoadFinished,
78
- onLoadFailed,
79
- shouldShowLoadingError,
80
- isAnyLoaded,
81
- arePreviousComponentsLoaded,
82
- } = useLoadingState(feed?.entry?.length || components.length);
83
-
84
- const theme = useTheme();
85
-
86
- const logTimestamp = useProfilerLogging();
87
-
88
73
  const riverComponents = React.useMemo(() => {
89
74
  if (feed?.entry?.length < components.length) {
90
75
  return R.slice(0, feed?.entry?.length, components);
@@ -93,55 +78,42 @@ function ComponentsMapComponent(props: Props) {
93
78
  return components;
94
79
  }, [components, feed]);
95
80
 
81
+ const logTimestamp = useProfilerLogging();
82
+
83
+ const onLoadDone = React.useCallback(() => {
84
+ logTimestamp(riverId?.toString());
85
+ }, [logTimestamp]);
86
+
87
+ const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
88
+ useLoadingState(riverComponents.length, onLoadDone);
89
+
90
+ const theme = useTheme();
91
+
96
92
  const renderRiverItem = React.useCallback(
97
93
  ({ item, index }) => {
98
- const readyToBeDisplayed = arePreviousComponentsLoaded(index);
99
-
100
- const styles = {
101
- display: !readyToBeDisplayed ? "none" : ("flex" as "none" | "flex"),
94
+ const riverItemProps = {
95
+ riverId,
96
+ item,
97
+ index,
98
+ isScreenWrappedInContainer,
99
+ feed,
100
+ groupId,
101
+ onLoadFailed,
102
+ onLoadFinished,
103
+ getStaticComponentFeed,
104
+ isLast: isLast(index, riverComponents.length),
105
+ loadingState,
102
106
  };
103
107
 
104
108
  return (
105
- <View style={styles}>
106
- <ScreenLoadingMeasurementsListItemWrapper index={index}>
107
- <RiverItem
108
- {...{
109
- readyToBeDisplayed,
110
- riverId,
111
- item,
112
- index,
113
- isScreenWrappedInContainer,
114
- feed,
115
- groupId,
116
- onLoadFailed,
117
- onLoadFinished,
118
- getStaticComponentFeed,
119
- isLast: isLast(index, riverComponents.length),
120
- }}
121
- />
122
- </ScreenLoadingMeasurementsListItemWrapper>
123
- </View>
109
+ <ScreenLoadingMeasurementsListItemWrapper index={index}>
110
+ <RiverItem {...riverItemProps} />
111
+ </ScreenLoadingMeasurementsListItemWrapper>
124
112
  );
125
113
  },
126
- [
127
- feed,
128
- getStaticComponentFeed,
129
- arePreviousComponentsLoaded,
130
- onLoadFailed,
131
- onLoadFinished,
132
- ]
114
+ [feed, getStaticComponentFeed, onLoadFailed, onLoadFinished]
133
115
  );
134
116
 
135
- const renderFooter = React.useCallback(() => {
136
- return (
137
- <RiverFooter
138
- visible={isAnyLoading}
139
- flatListHeight={flatListHeight}
140
- isAnyLoaded={isAnyLoaded || waitForAllComponents}
141
- />
142
- );
143
- }, [flatListHeight, isAnyLoading, isAnyLoaded]);
144
-
145
117
  const screenStyle = React.useMemo(
146
118
  () => ({
147
119
  paddingTop: ifEmptyUseFallback(
@@ -179,12 +151,6 @@ function ComponentsMapComponent(props: Props) {
179
151
 
180
152
  usePipesCacheReset(riverId, riverComponents);
181
153
 
182
- React.useEffect(() => {
183
- if (isAllLoaded) {
184
- logTimestamp(riverId?.toString());
185
- }
186
- }, [isAllLoaded]);
187
-
188
154
  const refreshControl = React.useMemo(
189
155
  () =>
190
156
  pullToRefreshEnabled ? (
@@ -280,6 +246,11 @@ function ComponentsMapComponent(props: Props) {
280
246
  }
281
247
  }, []);
282
248
 
249
+ const contentContainerStyle = React.useMemo(
250
+ () => (isScreenWrappedInContainer ? {} : screenStyle),
251
+ [isScreenWrappedInContainer, screenStyle]
252
+ );
253
+
283
254
  if (shouldShowLoadingError) {
284
255
  return <RiverError />;
285
256
  }
@@ -291,7 +262,7 @@ function ComponentsMapComponent(props: Props) {
291
262
  <View style={styles.container}>
292
263
  <ScreenLoadingMeasurements
293
264
  riverId={riverId}
294
- numberOfComponents={feed?.entry?.length || components.length}
265
+ numberOfComponents={riverComponents.length}
295
266
  >
296
267
  <ViewportTracker>
297
268
  <FlatList
@@ -312,10 +283,13 @@ function ComponentsMapComponent(props: Props) {
312
283
  keyExtractor={keyExtractor}
313
284
  renderItem={renderRiverItem}
314
285
  data={riverComponents}
315
- contentContainerStyle={
316
- isScreenWrappedInContainer ? {} : screenStyle
286
+ contentContainerStyle={contentContainerStyle}
287
+ ListFooterComponent={
288
+ <RiverFooter
289
+ flatListHeight={flatListHeight}
290
+ loadingState={loadingState}
291
+ />
317
292
  }
318
- ListFooterComponent={renderFooter}
319
293
  refreshControl={refreshControl}
320
294
  onScrollBeginDrag={onScrollBeginDrag}
321
295
  onScroll={onScroll}
@@ -1,50 +1,86 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
3
-
4
- const allTrue = R.all(R.equals(true));
5
- const anyFalse = R.any(R.equals(false));
6
- const anyTrue = R.any(R.equals(true));
2
+ import { isNil, set, lensIndex, T, slice } from "ramda";
3
+ import { BehaviorSubject } from "rxjs";
4
+ import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
7
5
 
8
6
  const reducer = (state, { payload }) => {
9
- if (!R.isNil(payload) && !state[payload]) {
10
- return R.set(R.lensIndex(payload), true)(state);
7
+ if (!isNil(payload) && !state[payload]) {
8
+ return set(lensIndex(payload), true)(state);
11
9
  }
12
10
 
13
11
  return state;
14
12
  };
15
13
 
16
- type Return = {
17
- isAnyLoading: boolean;
18
- isAllLoaded: boolean;
14
+ type LoadingState = {
15
+ index: number;
16
+ done: boolean;
19
17
  waitForAllComponents: boolean;
18
+ };
19
+
20
+ type Return = {
21
+ loadingState: BehaviorSubject<LoadingState>;
20
22
  onLoadFinished: (index: number) => void;
21
23
  onLoadFailed: ({ error, index }: { error: Error; index: number }) => void;
22
24
  shouldShowLoadingError: boolean;
23
- isAnyLoaded: boolean;
24
25
  arePreviousComponentsLoaded: (index: number) => boolean;
25
26
  };
26
27
 
27
- type Action = { payload: { index: number } };
28
-
29
- type Loaded = true;
30
- type Loading = false;
31
-
32
- type LoadingState = Array<Loaded | Loading>;
33
-
34
28
  // TODO: Take this value from Zapp configuration, when feature is added to GeneralScreen
35
29
  const SHOULD_FAIL_ON_COMPONENT_LOADING = false;
36
30
 
37
- export const useLoadingState = (count: number): Return => {
31
+ const createLoadingStateObservable = () =>
32
+ new BehaviorSubject<LoadingState>({
33
+ index: -1,
34
+ done: false,
35
+ waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
36
+ });
37
+
38
+ export const useLoadingState = (
39
+ count: number,
40
+ onLoadDone: () => void
41
+ ): Return => {
42
+ const componentStateRef = React.useRef(new Array(count).fill(false));
38
43
  const [loadingError, setLoadingError] = React.useState(null);
39
44
 
40
- const [componentsState, dispatch] = React.useReducer<
41
- React.Reducer<LoadingState, Action>
42
- >(reducer, new Array(count).fill(false));
45
+ const loadingState = useRefWithInitialValue<BehaviorSubject<LoadingState>>(
46
+ createLoadingStateObservable
47
+ );
48
+
49
+ const arePreviousComponentsLoaded = React.useCallback((index) => {
50
+ if (index === 0) {
51
+ return true;
52
+ }
43
53
 
44
- const handleComponentLoaded = React.useCallback((index) => {
45
- dispatch({ payload: index });
54
+ const componentsBefore = slice(0, index, componentStateRef.current);
55
+
56
+ return componentsBefore.every(T);
46
57
  }, []);
47
58
 
59
+ const dispatch = React.useCallback(({ payload }) => {
60
+ const newState = reducer(componentStateRef.current, { payload });
61
+ componentStateRef.current = newState;
62
+ const isDone = arePreviousComponentsLoaded(count - 1);
63
+
64
+ const state = loadingState.current.getValue();
65
+
66
+ const newLoadingState = {
67
+ ...state,
68
+ index: state.index < payload ? payload : state.index,
69
+ done: isDone,
70
+ };
71
+
72
+ loadingState.current.next(newLoadingState);
73
+
74
+ if (isDone) {
75
+ onLoadDone();
76
+ }
77
+ }, []);
78
+
79
+ const handleComponentLoaded = React.useCallback(
80
+ (index) => dispatch({ payload: index }),
81
+ []
82
+ );
83
+
48
84
  const handleComponentLoadErrorWhenNeedToFail = React.useCallback(
49
85
  ({ error }) => {
50
86
  if (error !== loadingError) {
@@ -55,35 +91,26 @@ export const useLoadingState = (count: number): Return => {
55
91
  );
56
92
 
57
93
  const handleComponentLoadErrorWhenNoNeedToFail = React.useCallback(
58
- ({ index }) => {
59
- handleComponentLoaded(index);
60
- },
94
+ ({ index }) => handleComponentLoaded(index),
61
95
  []
62
96
  );
63
97
 
64
- const arePreviousComponentsLoaded = React.useCallback(
65
- (index) => {
66
- if (index === 0) {
67
- return true;
68
- }
69
-
70
- const componentsBefore = R.slice(0, index, componentsState);
71
-
72
- return allTrue(componentsBefore);
73
- },
74
- [componentsState]
98
+ return React.useMemo(
99
+ () => ({
100
+ loadingState: loadingState.current,
101
+ onLoadFinished: handleComponentLoaded,
102
+ onLoadFailed: SHOULD_FAIL_ON_COMPONENT_LOADING
103
+ ? handleComponentLoadErrorWhenNeedToFail
104
+ : handleComponentLoadErrorWhenNoNeedToFail,
105
+ shouldShowLoadingError: SHOULD_FAIL_ON_COMPONENT_LOADING && loadingError,
106
+ arePreviousComponentsLoaded,
107
+ }),
108
+ [
109
+ loadingError,
110
+ handleComponentLoaded,
111
+ handleComponentLoadErrorWhenNeedToFail,
112
+ handleComponentLoadErrorWhenNoNeedToFail,
113
+ arePreviousComponentsLoaded,
114
+ ]
75
115
  );
76
-
77
- return {
78
- isAnyLoading: anyFalse(componentsState),
79
- isAllLoaded: allTrue(componentsState),
80
- onLoadFinished: handleComponentLoaded,
81
- onLoadFailed: SHOULD_FAIL_ON_COMPONENT_LOADING
82
- ? handleComponentLoadErrorWhenNeedToFail
83
- : handleComponentLoadErrorWhenNoNeedToFail,
84
- shouldShowLoadingError: SHOULD_FAIL_ON_COMPONENT_LOADING && loadingError,
85
- isAnyLoaded: anyTrue(componentsState),
86
- waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
87
- arePreviousComponentsLoaded,
88
- };
89
116
  };