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 6 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
15 changes: 8 additions & 7 deletions src/NoPrint.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ 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>;
}
}
const NoPrint = ({ children, force }) => {
const force_ = force ? s._force : '';
return <div className={`${s.root} ${force_}`}>{children}</div>;
};

NoPrint.propTypes = propTypes;

export default NoPrint;

96 changes: 41 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,63 @@ 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() {
const 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;
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() {
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>;
};

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>;
}
}
Print.propTypes = propTypes;
Print.contextTypes = contextTypes;

export default Print;
174 changes: 89 additions & 85 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 createRender = children => {
const el = document.createElement('div');
el.id = 'render';
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 = {};
window.matchMedia('print').onchange = () => {
debug('toggle print mode', window.matchMedia('print').matches);
this.setState({ isInPrintPreview: window.matchMedia('print').matches });
};
const deleteRender = () => {
const el = document.getElementById('render');
el && document.body.removeChild(el);
};

this.hasSingle = false;
}
const PrintProvider = props => {

getChildContext() {
return {
printProvider: {
isPrint: window.matchMedia('print').matches,
regPrintable: this.regPrintable.bind(this),
unregPrintable: this.unregPrintable.bind(this),
},
};
}
const [isInPrintPreview, setIsInPrintPreview] = useState(false);
const [printableNodes, setPrintableNodes ] = useState([]);

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

// 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();
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]),
});
this.printableRegistry = Object.assign({}, this.printableRegistry, { [key]: undefined });
};

const unregPrintable = (key, isSingle) => {
const loose = props.loose || props.invert;
if (
printableRegistry[key] === undefined ||
isInPrintPreview ||
loose
)
return;
setPrintableNodes(
spliced(
printableNodes,
printableRegistry[key]
)
);
printableRegistry.current = {
...printableRegistry,
[key]: undefined
};
if (isSingle) {
this.unhideAll();
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;
useEffect(() => {
window.matchMedia('print').onchange = () => {
debug('toggle print mode', window.matchMedia('print').matches);
setIsInPrintPreview(window.matchMedia('print').matches);
};
});

useEffect(() => {
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>);
createRender(<div>{children}</div>);
}
setTimeout(() => this.deleteRender(), 0);

setTimeout(() => deleteRender(), 0);
});

useEffect(() => {
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>;
}
}
});

const loose = props.loose || props.invert;
const loose_ = loose ? s.loose : '';
const invert_ = props.invert ? s.invert : '';
return (
<PrintProviderContext.Provider unregPrintable={unregPrintable} regPrintable={regPrintable}>
<div className={`${s.wrap} ${loose_} ${invert_} `}>
{props.children}
</div>
</PrintProviderContext.Provider>
);
};

export default PrintProvider;

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;