Skip to content

Commit

Permalink
add theme manager context for changing theme easier
Browse files Browse the repository at this point in the history
  • Loading branch information
thundermiracle committed Oct 24, 2019
1 parent b8717a9 commit d1f8aa5
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 11 deletions.
2 changes: 1 addition & 1 deletion pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ Layout.defaultProps = {
title: '',
};

export default makeNextApp(null, Layout);
export default makeNextApp(null, Layout, true, true);
39 changes: 31 additions & 8 deletions pages/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import React from 'react';
import Button from '@material-ui/core/Button';
import red from '@material-ui/core/colors/red';
import lime from '@material-ui/core/colors/lime';
import Link from 'next/link';

const Index = () => (
<div>
<Button color="primary">Hello World</Button>
<Link href="/page1">
<Button>To Page1</Button>
</Link>
</div>
);
import { useThemeManagerContext } from '../src';

const theme2 = {
palette: {
primary: red,
secondary: lime,
},
};

const Index = () => {
const { setTheme } = useThemeManagerContext();
const handleChangeTheme = () => {
setTheme(theme2, () => {
console.log('changed to theme2!');
});
};

return (
<div>
<Button color="primary">Hello World</Button>
<Button color="secondary" variant="outlined" onClick={handleChangeTheme}>
Change Theme
</Button>
<Link href="/page1">
<Button>To Page1</Button>
</Link>
</div>
);
};

export default Index;
73 changes: 73 additions & 0 deletions src/context/ThemeManagerContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';

const initState = {
theme: {},
// eslint-disable-next-line no-unused-vars
setTheme: (theme, callback) => {},
};

const ThemeManagerContext = React.createContext(initState);

class ThemeManagerProvider extends React.PureComponent {
constructor(props) {
super(props);

this.state = {
theme: props.initTheme || initState.theme,
};
}

setTheme = (theme, callback) => {
this.setState({ theme }, () => {
if (callback && typeof callback === 'function') {
callback(theme);
}
});
};

render() {
const { children } = this.props;
const { theme } = this.state;

return (
<ThemeManagerContext.Provider
value={{
theme,
setTheme: this.setTheme,
}}
>
{children}
</ThemeManagerContext.Provider>
);
}
}

ThemeManagerProvider.propTypes = {
children: PropTypes.any.isRequired,
initTheme: PropTypes.object,
};

ThemeManagerProvider.defaultProps = {
initTheme: {},
}

const useThemeManagerContext = () => {
return useContext(ThemeManagerContext);
};

const withThemeManager = BaseComponent => {
const WithThemeManager = props => {
return (
<ThemeManagerContext.Consumer>
{ctx => {
return <BaseComponent {...ctx} {...props} />;
}}
</ThemeManagerContext.Consumer>
);
};

return WithThemeManager;
};

export { ThemeManagerContext as default, ThemeManagerProvider, useThemeManagerContext, withThemeManager };
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export {
DefaultNextDocument,
} from './nextjs/makeNextDocument';
export { default as makeNextApp, DefaultNextApp } from './nextjs/makeNextApp';
export { useThemeManagerContext, withThemeManager } from './context/ThemeManagerContext';
export { default as deepCompareObj } from './util/deepCompareObj';
21 changes: 19 additions & 2 deletions src/nextjs/makeNextApp.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from 'react';
import App from 'next/app';
import withParts from '../mui/withParts';
import ThemeManagerContext, {
ThemeManagerProvider,
} from '../context/ThemeManagerContext';

const makeNextApp = (
muiTheme,
Expand All @@ -26,9 +29,23 @@ const makeNextApp = (

render() {
const { Component, pageProps } = this.props;
const NewComponent = hocs(Component);

return <NewComponent {...pageProps} />;
return (
<ThemeManagerProvider initTheme={muiTheme}>
<ThemeManagerContext.Consumer>
{({ theme }) => {
const NewComponent = withParts(
theme,
Layout,
enableNProgress,
enableDefaultCssBaseline,
)(Component);

return <NewComponent {...pageProps} />;
}}
</ThemeManagerContext.Consumer>
</ThemeManagerProvider>
);
}
}

Expand Down
35 changes: 35 additions & 0 deletions src/util/deepCompareObj.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export const sameArray = (arr1, arr2) => {
return arr1.length === arr2.length && arr1.every(v => arr2.indexOf(v) > -1);
};

/**
* Deep Check if two object is same or not
*
* @param {*} obj1
* @param {*} obj2
*/
export default function deepCompareObj(obj1, obj2) {
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return false;
}

const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);

// compare keys first
if (!sameArray(obj1Keys, obj2Keys)) return false;

return obj1Keys.every(key => {
if (Array.isArray(obj1[key])) {
if (!sameArray(obj1[key], obj2[key])) {
return false;
}
} else if (typeof obj1[key] === 'object') {
return deepCompareObj(obj1[key], obj2[key]);
} else if (obj1[key] !== obj2[key]) {
return false;
}

return true;
});
}
66 changes: 66 additions & 0 deletions test/util/deepCompareObj.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import deepCompareObj from '../../src/util/deepCompareObj';

test('input is not object', () => {
const obj1 = { a: 1 };
const obj2 = 'a1';

const result1 = deepCompareObj(obj1, obj2);
const result2 = deepCompareObj(obj2, obj1);

expect(result1).toEqual(false);
expect(result2).toEqual(false);
});

test('input empty', () => {
const obj1 = {};
const obj2 = {};

const result = deepCompareObj(obj1, obj2);

expect(result).toEqual(true);
});

test('input have nested object', () => {
const obj1 = { a: { b: { b1: 111, b2: 222 } }, c: 'hello' };
const obj2 = { c: 'hello', a: { b: { b2: 222, b1: 111 } } };

const result = deepCompareObj(obj1, obj2);

expect(result).toEqual(true);
});

test('input have array value', () => {
const obj1 = { a: [1, 2, 3] };
const obj2 = { a: [2, 3, 1] };

const result = deepCompareObj(obj1, obj2);

expect(result).toEqual(true);
});

test('input have different array value', () => {
const obj1 = { a: [1, 2, 3] };
const obj2 = { a: [2, 3, 4] };

const result = deepCompareObj(obj1, obj2);

expect(result).toEqual(false);
});

test('input have different object value', () => {
const obj1 = { a: { b: 1 } };
const obj2 = { a: { c: 1 } };

const result = deepCompareObj(obj1, obj2);

expect(result).toEqual(false);
});

test('input have different normal value', () => {
const obj1 = { a: 1 };
const obj2 = { a: '1' };

const result = deepCompareObj(obj1, obj2);

expect(result).toEqual(false);
});

0 comments on commit d1f8aa5

Please sign in to comment.