Skip to content

Refactor/class to func component #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^10.0.3",
"babel-plugin-external-helpers": "^6.22.0",
"babel-plugin-transform-import-css": "^0.1.6",
"babel-plugin-transform-react-remove-prop-types": "^0.4.6",
Expand All @@ -32,7 +33,7 @@
"eslint": "^4.2.0",
"eslint-plugin-react": "^7.1.0",
"prop-types": "^15.6.1",
"react": "^16.4.0",
"react": "^16.8.0",
"react-dom": "^16.4.0",
"rollup": "^0.60.7",
"rollup-plugin-babel": "^3.0.4",
Expand Down
10 changes: 4 additions & 6 deletions src/NoPrint.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ const propTypes = {
children: PropTypes.node.isRequired,
};

export default class NoPrint extends React.PureComponent {
render() {
const { children, force } = this.props;
const force_ = force ? s._force : '';
return <div className={`${s.root} ${force_}`}>{children}</div>;
}
export default function NoPrint ({ children, force }) {
const force_ = force ? s._force : '';
return <div className={`${s.root} ${force_}`}>{children}</div>;
}

NoPrint.propTypes = propTypes;
94 changes: 39 additions & 55 deletions src/Print.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { usePrintProvider } from './PrintProviderContext';
import PropTypes from 'prop-types';

import PrintProvider from './PrintProvider';
import { debug } from './common';
import s from './Print.css';

Expand All @@ -13,76 +12,61 @@ const propTypes = {
exclusive: PropTypes.bool,
printOnly: PropTypes.bool
};
const contextTypes = PrintProvider.childContextTypes;

export default class Print extends React.Component {

constructor(props) {
super(props);

this.state = {
printOffsetLeft: 0,
printOffsetTop: 0
};
}
componentDidMount() {
export default function Print (props) {
const [printOffsetLeft, setPrintOffsetLeft] = useState(0);
const [printOffsetTop, setPrintOffsetTop] = useState(0);
const { regPrintable, unregPrintable } = usePrintProvider();
const printElement = useRef(null);
const main_ = (props.main || props.single) ? s._main : '';
const excl_ = (props.exclusive || props.printOnly) ? s._exclusive : '';
const isPrint = window.matchMedia('print').matches;
const offset_ = ((printOffsetTop || printOffsetLeft) && main_ && isPrint) ?
{ marginTop: -printOffsetTop, marginLeft: -printOffsetLeft } : {};
const globalClassName = 'react-easy-print-print'; // using in hiddenAll
const className = `${globalClassName} ${s.root} ${main_} ${excl_}`;

if (this.props.name) {
debug('init printable', this.props.name);
const isSingle = (this.props.main || this.props.single);
this.context.printProvider && this.context.printProvider.regPrintable(this.props.name, <Print {...this.props} />, isSingle);
useEffect(() => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this useEffect should be componentDidMount equivalent, because, e.g. window.matchMedia('print').onchange must be called at once,

so, as I understand, we need add empty list of dependencies ([]) here: useEffect(() => {...}, []);

Copy link
Owner

@a-x- a-x- Nov 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, we need to verify all the useEffects' dependencies

if (props.name) {
debug('init printable', props.name);
const isSingle = (props.main || props.single);
regPrintable(name, <Print {...props} />, isSingle);
}

if (this.props.main || this.props.single) {
if (props.main || props.single) {

window.matchMedia('print').onchange = () => {
const isPrint = window.matchMedia('print').matches;

if (isPrint) {
const bodyRect = document.body.getBoundingClientRect();
const elem = this.printElement;
const elem = printElement.current;
const elemRect = elem && elem.getBoundingClientRect();
const printOffsetLeft = elemRect && (elemRect.left - bodyRect.left);
const printOffsetTop = elemRect && (elemRect.top - bodyRect.top);

this.setState({
printOffsetTop,
printOffsetLeft,
});
setPrintOffsetLeft(printOffsetLeft);
setPrintOffsetTop(printOffsetTop);
} else {
this.setState({
printOffsetTop: 0,
printOffsetLeft: 0
})
setPrintOffsetLeft(0);
setPrintOffsetTop(0);
}
};
}
}

componentWillUnmount() {
if (this.props.name) {
debug('remove printable', this.props.name);
const isSingle = (this.props.main || this.props.single);
this.context.printProvider && this.context.printProvider.unregPrintable(this.props.name, isSingle);
}

if (this.props.main || this.props.single) {
window.matchMedia('print').onchange = null;
}
}

render() {

const { children, main, single, exclusive, printOnly } = this.props;
const { printOffsetLeft, printOffsetTop } = this.state;
const main_ = (main || single) ? s._main : '';
const excl_ = (exclusive || printOnly) ? s._exclusive : '';
const isPrint = window.matchMedia('print').matches;
const offset_ = ((printOffsetTop || printOffsetLeft) && main_ && isPrint) ? { marginTop: -printOffsetTop, marginLeft: -printOffsetLeft } : {};
const globalClassName = 'react-easy-print-print'; // using in hiddenAll
const className = `${globalClassName} ${s.root} ${main_} ${excl_}`;
return <div ref={(el) => this.printElement = el} style={offset_} className={className}>{children}</div>;
}
return () => {
if (props.name) {
debug('remove printable', props.name);
const isSingle = (props.main || props.single);
unregPrintable(props.name, isSingle);
}

if (props.main || props.single) {
window.matchMedia('print').onchange = null;
}
};
}, []);
return <div ref={printElement} style={offset_} className={className}>{props.children}</div>;
}

Print.propTypes = propTypes;
Print.contextTypes = contextTypes;
184 changes: 94 additions & 90 deletions src/PrintProvider.jsx
Original file line number Diff line number Diff line change
@@ -1,125 +1,129 @@
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import PrintProviderContext from './PrintProviderContext';
import s from './PrintProvider.css';
import { spliced, debug } from './common';

export const propTypes = {
loose: PropTypes.bool,
children: PropTypes.node.isRequired,
invert: PropTypes.bool,
invert: PropTypes.bool
};

export const childContextTypes = {
printProvider: PropTypes.shape({
regPrintable: PropTypes.func.isRequired,
unregPrintable: PropTypes.func.isRequired,
isPrint: PropTypes.bool.isRequired,
}).isRequired,
const ID = 'react-easy-print-render';

const createRender = children => {
const el = document.createElement('div');
el.id = ID;
document.body.appendChild(el);
ReactDOM.render(<div className={s.printRender}>{children}</div>, el);
};

export default class PrintProvider extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isInPrintPreview: false,
printableNodes: [],
};
this.printableRegistry = {};
const deleteRender = () => {
const el = document.getElementById(ID);
el && document.body.removeChild(el);
};

export default function PrintProvider (props) {

const [isInPrintPreview, setIsInPrintPreview] = useState(false);
const [printableNodes, setPrintableNodes ] = useState([]);

const printableRegistry = useRef({});
const hasSingle = useRef(false);

const loose = props.loose || props.invert;
const loose_ = loose ? s.loose : '';
const invert_ = props.invert ? s.invert : '';

useEffect(() => {
window.matchMedia('print').onchange = () => {
debug('toggle print mode', window.matchMedia('print').matches);
this.setState({ isInPrintPreview: window.matchMedia('print').matches });
setIsInPrintPreview(window.matchMedia('print').matches);
};
}, []);

this.hasSingle = false;
}
useEffect(() => {
if (isInPrintPreview && printableNodes.length && !loose && !hasSingle.current) {
debug('render printable only', printableNodes);
const children = React.Children.map(printableNodes, (child, key) => {
return React.cloneElement(child, { key });
});
createRender(<div>{children}</div>);
}
setTimeout(() => deleteRender(), 0);
});

getChildContext() {
return {
printProvider: {
isPrint: window.matchMedia('print').matches,
regPrintable: this.regPrintable.bind(this),
unregPrintable: this.unregPrintable.bind(this),
},
};
}
useEffect(() => {
debug('render everything', isInPrintPreview, printableNodes.length, !loose);
}, [isInPrintPreview, printableNodes.length, loose]);

// hideAll - is being used to cover all of React Portals, popups and modals and etc.
hideAll() {
const hideAll = () => {
document.body.classList.add(s.hiddenAll);
this.hasSingle = true;
}
hasSingle.current = true;
};

unhideAll() {
const unhideAll = () => {
document.body.classList.remove(s.hiddenAll);
this.hasSingle = false;
}
hasSingle.current = false;
};

regPrintable(key, node) {
const loose = this.props.loose || this.props.invert;
const regPrintable = (key, node) => {
const loose = props.loose || props.invert;
const isSingle = node.props.single || node.props.main;
const { hasSingle } = this;

debug('reg printable', key, node);

if (this.printableRegistry[key] !== undefined || loose) return;
setTimeout(() => this.setState({
printableNodes: this.state.printableNodes.concat(node),
}), 0);
this.printableRegistry[key] = this.state.printableNodes.length;
if (printableRegistry.current[key] !== undefined || loose) return;
setTimeout(
() =>
setPrintableNodes(printableNodes.concat(node)),
0
);
printableRegistry.current[key] = printableNodes.length;

if (isSingle && !hasSingle) {
this.hideAll();
if (isSingle && !hasSingle.current) {
hideAll();
} else if (isSingle) {
console.warn(new Error('react-easy-print warning \n\t you\'re using more than one `single` Print component'));
console.warn(
new Error(
'react-easy-print warning \n\t you\'re using more than one `single` Print component'
)
);
}
}

unregPrintable(key, isSingle) {
const loose = this.props.loose || this.props.invert;
if (this.printableRegistry[key] === undefined || this.state.isInPrintPreview || loose) return;
this.setState({
printableNodes: spliced(this.state.printableNodes, this.printableRegistry[key]),
};

const unregPrintable = (key, isSingle) => {
const loose = props.loose || props.invert;
if (
printableRegistry.current[key] === undefined ||
isInPrintPreview ||
loose
)
return;
setPrintableNodes(
spliced(
printableNodes,
printableRegistry.current[key]
)
);
printableRegistry.current = Object.assign({}, printableRegistry.current, {
[key]: undefined
});
this.printableRegistry = Object.assign({}, this.printableRegistry, { [key]: undefined });
if (isSingle) {
this.unhideAll();
}
}

createRender(children) {
const el = document.createElement('div');
el.id = 'render';
document.body.appendChild(el);

ReactDOM.render(<div className={s.printRender} >{children}</div>, el);
}

deleteRender() {
const el = document.getElementById('render');
el && document.body.removeChild(el);
}

render() {
const { isInPrintPreview, printableNodes } = this.state;
const loose = this.props.loose || this.props.invert;
const { hasSingle } = this;

if (isInPrintPreview && printableNodes.length && !loose && !hasSingle) {
debug('render printable only', printableNodes);
const children = React.Children.map(printableNodes, (child, key) => {
return React.cloneElement(child, { key });
});

this.createRender(<div>{children}</div>);
unhideAll();
}
setTimeout(() => this.deleteRender(), 0);
};

return (
<PrintProviderContext.Provider unregPrintable={unregPrintable} regPrintable={regPrintable}>
<div className={`${s.wrap} ${loose_} ${invert_} `}>
{props.children}
</div>
</PrintProviderContext.Provider>
);
};

debug('render everything', isInPrintPreview, printableNodes.length, !loose);
const loose_ = loose ? s.loose : '';
const invert_ = this.props.invert ? s.invert : '';
return <div className={`${s.wrap} ${loose_} ${invert_} `}>{this.props.children}</div>;
}
}
PrintProvider.propTypes = propTypes;
PrintProvider.childContextTypes = childContextTypes;
15 changes: 15 additions & 0 deletions src/PrintProviderContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import React, { useContext } from 'react';

const PrintProviderContext = React.createContext();

export const usePrintProvider = () => {
const { regPrintable, unregPrintable } = useContext(PrintProviderContext);

return {
regPrintable,
unregPrintable
};
};

export default PrintProviderContext;