@khanacademy/wonder-blocks-core 4.2.1 → 4.3.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @khanacademy/wonder-blocks-core
2
2
 
3
+ ## 4.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 246a921d: NEW: `useForceUpdate` hook. This should rarely be used and likely only ever from other hooks.
8
+
3
9
  ## 4.2.1
4
10
 
5
11
  ## 4.2.0
package/dist/es/index.js CHANGED
@@ -682,6 +682,25 @@ const useUniqueIdWithoutMock = scope => {
682
682
  return idFactory.current;
683
683
  };
684
684
 
685
+ /**
686
+ * Hook for forcing a component to update on demand.
687
+ *
688
+ * This is for use inside other hooks that do some advanced
689
+ * trickery with storing state outside of React's own state
690
+ * mechanisms. As such this should never be called directly
691
+ * outside of a hook, and more often than not, is the wrong
692
+ * choice for whatever you are trying to do. If in doubt,
693
+ * don't use it.
694
+ *
695
+ * @returns {() => void} A function that forces the component to update.
696
+ */
697
+
698
+ const useForceUpdate = () => {
699
+ const [, setState] = React.useState(false);
700
+ const forceUpdate = React.useCallback(() => setState(state => !state), []);
701
+ return forceUpdate;
702
+ };
703
+
685
704
  const {
686
705
  useContext,
687
706
  useEffect,
@@ -716,4 +735,4 @@ RenderStateRoot.defaultProps = {
716
735
  throwIfNested: true
717
736
  };
718
737
 
719
- export { IDProvider, RenderStateRoot, server as Server, Text, UniqueIDProvider, View, WithSSRPlaceholder, addStyle, useUniqueIdWithMock, useUniqueIdWithoutMock };
738
+ export { IDProvider, RenderStateRoot, server as Server, Text, UniqueIDProvider, View, WithSSRPlaceholder, addStyle, useForceUpdate, useUniqueIdWithMock, useUniqueIdWithoutMock };
package/dist/index.js CHANGED
@@ -82,7 +82,7 @@ module.exports =
82
82
  /******/
83
83
  /******/
84
84
  /******/ // Load entry module and return exports
85
- /******/ return __webpack_require__(__webpack_require__.s = 15);
85
+ /******/ return __webpack_require__(__webpack_require__.s = 16);
86
86
  /******/ })
87
87
  /************************************************************************/
88
88
  /******/ ([
@@ -101,7 +101,7 @@ module.exports = require("react");
101
101
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
102
102
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
103
103
 
104
- const RenderState = __webpack_require__(17)({
104
+ const RenderState = __webpack_require__(18)({
105
105
  Root: "root",
106
106
  Initial: "initial",
107
107
  Standard: "standard"
@@ -573,7 +573,7 @@ function processStyleList(style) {
573
573
  className: aphrodite__WEBPACK_IMPORTED_MODULE_0__["css"].apply(void 0, stylesheetStyles)
574
574
  };
575
575
  }
576
- /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(16)))
576
+ /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(17)))
577
577
 
578
578
  /***/ }),
579
579
  /* 8 */
@@ -917,6 +917,34 @@ let serverSide = false;
917
917
  /* 14 */
918
918
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
919
919
 
920
+ "use strict";
921
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useForceUpdate; });
922
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
923
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
924
+
925
+ /**
926
+ * Hook for forcing a component to update on demand.
927
+ *
928
+ * This is for use inside other hooks that do some advanced
929
+ * trickery with storing state outside of React's own state
930
+ * mechanisms. As such this should never be called directly
931
+ * outside of a hook, and more often than not, is the wrong
932
+ * choice for whatever you are trying to do. If in doubt,
933
+ * don't use it.
934
+ *
935
+ * @returns {() => void} A function that forces the component to update.
936
+ */
937
+
938
+ const useForceUpdate = () => {
939
+ const [, setState] = react__WEBPACK_IMPORTED_MODULE_0__["useState"](false);
940
+ const forceUpdate = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](() => setState(state => !state), []);
941
+ return forceUpdate;
942
+ };
943
+
944
+ /***/ }),
945
+ /* 15 */
946
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
947
+
920
948
  "use strict";
921
949
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RenderStateRoot; });
922
950
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
@@ -963,7 +991,7 @@ RenderStateRoot.defaultProps = {
963
991
  };
964
992
 
965
993
  /***/ }),
966
- /* 15 */
994
+ /* 16 */
967
995
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
968
996
 
969
997
  "use strict";
@@ -994,8 +1022,12 @@ __webpack_require__.r(__webpack_exports__);
994
1022
 
995
1023
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useUniqueIdWithoutMock", function() { return _hooks_use_unique_id_js__WEBPACK_IMPORTED_MODULE_7__["b"]; });
996
1024
 
997
- /* harmony import */ var _components_render_state_root_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(14);
998
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RenderStateRoot", function() { return _components_render_state_root_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
1025
+ /* harmony import */ var _hooks_use_force_update_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(14);
1026
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useForceUpdate", function() { return _hooks_use_force_update_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
1027
+
1028
+ /* harmony import */ var _components_render_state_root_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(15);
1029
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RenderStateRoot", function() { return _components_render_state_root_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
1030
+
999
1031
 
1000
1032
 
1001
1033
 
@@ -1008,7 +1040,7 @@ __webpack_require__.r(__webpack_exports__);
1008
1040
 
1009
1041
 
1010
1042
  /***/ }),
1011
- /* 16 */
1043
+ /* 17 */
1012
1044
  /***/ (function(module, exports) {
1013
1045
 
1014
1046
  var g;
@@ -1034,7 +1066,7 @@ module.exports = g;
1034
1066
 
1035
1067
 
1036
1068
  /***/ }),
1037
- /* 17 */
1069
+ /* 18 */
1038
1070
  /***/ (function(module, exports, __webpack_require__) {
1039
1071
 
1040
1072
  "use strict";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-core",
3
- "version": "4.2.1",
3
+ "version": "4.3.0",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,54 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {render, act} from "@testing-library/react";
4
+ import {renderHook} from "@testing-library/react-hooks";
5
+
6
+ import {useForceUpdate} from "../use-force-update.js";
7
+
8
+ describe("#useForceUpdate", () => {
9
+ it("should return a function", () => {
10
+ // Arrange
11
+
12
+ // Act
13
+ const {
14
+ result: {current: result},
15
+ } = renderHook(() => useForceUpdate());
16
+
17
+ // Assert
18
+ expect(result).toBeInstanceOf(Function);
19
+ });
20
+
21
+ describe("returned function", () => {
22
+ beforeEach(() => {
23
+ jest.useFakeTimers();
24
+ });
25
+
26
+ it("should cause component to render", () => {
27
+ // Arrange
28
+ const Component = (props): React.Node => {
29
+ const countRef = React.useRef(0);
30
+ const forceUpdate = useForceUpdate();
31
+ React.useEffect(() => {
32
+ countRef.current++;
33
+
34
+ setTimeout(forceUpdate, 50);
35
+ });
36
+ return countRef.current;
37
+ };
38
+
39
+ // Act
40
+ const wrapper = render(<Component />);
41
+ act(() => {
42
+ // Advance enough for the timeout to run 4 times.
43
+ // Which means the component should have rendered 4 times,
44
+ // with one more pending for the timeout that was setup in
45
+ // the last render.
46
+ jest.advanceTimersByTime(204);
47
+ });
48
+ const result = wrapper.container.textContent;
49
+
50
+ // Assert
51
+ expect(result).toBe("4");
52
+ });
53
+ });
54
+ });
@@ -0,0 +1,23 @@
1
+ // @flow
2
+ import * as React from "react";
3
+
4
+ /**
5
+ * Hook for forcing a component to update on demand.
6
+ *
7
+ * This is for use inside other hooks that do some advanced
8
+ * trickery with storing state outside of React's own state
9
+ * mechanisms. As such this should never be called directly
10
+ * outside of a hook, and more often than not, is the wrong
11
+ * choice for whatever you are trying to do. If in doubt,
12
+ * don't use it.
13
+ *
14
+ * @returns {() => void} A function that forces the component to update.
15
+ */
16
+ export const useForceUpdate = (): (() => void) => {
17
+ const [, setState] = React.useState(false);
18
+ const forceUpdate = React.useCallback(
19
+ () => setState((state) => !state),
20
+ [],
21
+ );
22
+ return forceUpdate;
23
+ };
package/src/index.js CHANGED
@@ -12,6 +12,7 @@ export {
12
12
  useUniqueIdWithMock,
13
13
  useUniqueIdWithoutMock,
14
14
  } from "./hooks/use-unique-id.js";
15
+ export {useForceUpdate} from "./hooks/use-force-update.js";
15
16
  export {RenderStateRoot} from "./components/render-state-root.js";
16
17
 
17
18
  export type {AriaProps, IIdentifierFactory, StyleType};