Skip to content

Commit

Permalink
Change helmetStore to ES Class
Browse files Browse the repository at this point in the history
  • Loading branch information
kouhin committed May 21, 2019
1 parent 77cf9d9 commit 9eec6a3
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 45 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-safety-helmet",
"description": "A fork of react-helmet that support for renderToNodeStream and thread safe, provides both react hooks and declarative api",
"version": "6.3.0",
"version": "7.0.0-beta.1",
"main": "./lib/Helmet.js",
"module": "es/Helmet.js",
"sideEffect": false,
Expand All @@ -16,8 +16,8 @@
"url": "https://github.com/openameba/react-safety-helmet"
},
"keywords": [
"react-helmet",
"nfl",
"react",
"helmet",
"react",
"hook",
"document",
Expand Down Expand Up @@ -47,6 +47,7 @@
"@babel/plugin-external-helpers": "^7.2.0",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/plugin-proposal-object-rest-spread": "^7.4.4",
"@babel/plugin-proposal-private-methods": "^7.4.4",
"@babel/plugin-transform-strict-mode": "^7.2.0",
"@babel/preset-env": "^7.4.4",
"@babel/preset-react": "^7.0.0",
Expand Down
114 changes: 72 additions & 42 deletions src/Helmet.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,63 +30,86 @@ const useIsomorphicEffect = ExecutionEnvironment.canUseDOM
const HelmetContext = createContext();
HelmetContext.displayName = "HelmetContext";

const createHelmetStore = subscribe => {
const store = {
state: rootReducer()
};
store.peek = () => {
class HelmetStore {
#state;
#peekCache;
#canUseDOM;
#subscribe;
#prevReducedState;

constructor(opts = {}) {
this.#state = rootReducer();
this.#peekCache = null;
this.#prevReducedState = null;
this.#subscribe = opts.subscribe;
this.#canUseDOM = opts.canUseDOM || ExecutionEnvironment.canUseDOM;
}

peek = () => {
if (
!store.peekCache ||
!store.peekCache.key ||
store.peekCache.key !== store.state
!this.#peekCache ||
!this.#peekCache.key ||
this.#peekCache.key !== this.#state
) {
store.peekCache = {
key: store.state,
value: reducePropsToState(store.state.propsList)
this.#peekCache = {
key: this.#state,
value: reducePropsToState(this.#state.propsList)
};
}
return store.peekCache.value;
return this.#peekCache.value;
};

renderStatic = () => {
return mapStateOnServer(this.peek());
};
store.renderStatic = () => mapStateOnServer(store.peek());
store.setState = (state, action) => {
if (state !== store.state) {
store.state = state;
if (subscribe) subscribe(action);

setState = (state, action) => {
if (state !== this.#state) {
this.#state = state;
if (this.#subscribe) this.#subscribe(action);
return true;
}
return false;
};
return store;

getState = () => {
return this.#state;
};

dispatch = action => {
const nextState = rootReducer(this.#state, action);
if (this.setState(nextState, action)) {
if (this.#canUseDOM) {
const nextReducedState = reducePropsToState(
this.#state.propsList
);
if (!deepEqual(this.#prevReducedState, nextReducedState)) {
this.#prevReducedState = nextReducedState;
handleClientStateChange(nextReducedState);
}
}
}
return nextState;
};
}

const createHelmetStore = subscribe => {
return new HelmetStore({
subscribe
});
};

function HelmetProvider({
canUseDOM = ExecutionEnvironment.canUseDOM,
children,
store = createHelmetStore()
store
}) {
const prevReducedState = useRef(null);
const dispatch = useCallback(
action => {
const nextState = rootReducer(store.state, action);
if (store.setState(nextState, action)) {
if (canUseDOM) {
const nextReducedState = reducePropsToState(
store.state.propsList
);
if (
!deepEqual(prevReducedState.current, nextReducedState)
) {
prevReducedState.current = nextReducedState;
handleClientStateChange(nextReducedState);
}
}
}
return nextState;
},
[canUseDOM, store]
);
const helmetStore = useMemo(() => store || createHelmetStore({canUseDOM}), [
store,
canUseDOM
]);
return (
<HelmetContext.Provider value={dispatch}>
<HelmetContext.Provider value={helmetStore.dispatch}>
{children}
</HelmetContext.Provider>
);
Expand All @@ -97,9 +120,11 @@ if (process.env.NODE_ENV !== "production") {
canUseDOM: PropTypes.bool,
children: PropTypes.node,
store: PropTypes.shape({
current: PropTypes.object,
dispatch: PropTypes.func,
getState: PropTypes.func,
peek: PropTypes.func,
renderStatic: PropTypes.func,
setState: PropTypes.func,
subscribe: PropTypes.func
})
};
Expand Down Expand Up @@ -290,6 +315,11 @@ function generateUniqueString() {
function useHelmet(props = {}) {
const instance = useMemo(() => generateUniqueString(), []);
const dispatch = useContext(HelmetContext);
if (!dispatch) {
throw new Error(
"You should not use useHelmet() or <Helmet> outside a <HelmetProvider>."
);
}
const called = useRef(false);
const prevProps = useRef();
const sideEffect = useCallback(() => {
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,14 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.2.0"

"@babel/plugin-proposal-private-methods@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.4.4.tgz#307b7db29d8ae2d259e7c0e6e665b1922d7ac856"
integrity sha512-EIV4fDVP3XwdizJ/H6308Km+d8xdLAUCAvsY8mjxhat9I3vNgssGhZuhgn/jw7IK5/91sN8PHtVGxMjeTSrSng==
dependencies:
"@babel/helper-create-class-features-plugin" "^7.4.4"
"@babel/helper-plugin-utils" "^7.0.0"

"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78"
Expand Down

0 comments on commit 9eec6a3

Please sign in to comment.