0

I have a Home component that renders with a map function an array of contacts, each contact has its own print button and should print only its contact . The issue is that currently it prints the entire page and not just the contact which the print was clicked on.

I added a isPrinting state to the contact component, by default set to false, when the print button is being clicked it's being set to true and when the print dialog is being closed I set it back to false.

How can I actually make the print button to print only the contact that its print was click?

My handlePrint function on contact component:

  handlePrint = () => {
    this.setState({ isPrinting: true });
    window.print();
    window.onafterprint(
      this.setState({ isPrinting: false })
    );
  };

map function on Home component:

  contacts.map(info => (
              <SearchResultsPanel
                info={info}
                key={info.id}
              />

I'm using '@media print' to control the print styles.

user3378165
  • 6,546
  • 17
  • 62
  • 101
  • Have you tried using setState callback in `this.setState({ isPrinting: true });`? – blaz Apr 02 '19 at 08:48
  • @blaz the `setState` is working as expected, when the user clicks on print the state changes to `true` and when he closes the print dialog it changes back to `false`, I'm asking here how to use this state to actually print only one of the contacts. – user3378165 Apr 02 '19 at 08:50
  • I asked above question since setState is asynchronous: https://stackoverflow.com/questions/42038590/when-to-use-react-setstate-callback - so there is no guarantee that the state is set yet at the time `window.print()` is executed. – blaz Apr 02 '19 at 08:54
  • The best solution could be to open contact alone in new window – r g Apr 02 '19 at 09:06

1 Answers1

1

What I would do is to by default hide on @media print every Contact component you display.

Then into your <SearchResultsPanel> component instance that you want to print out make append a css class that would override the display: none declaration that affect all the contact, using the isPrinting state you setup in this specific instance.

You also may want to use callbacks on setState to make sure the re-render caused by this.setState({ isPrinting: true }); is done before the window.print() is triggered.

So you could do :

handlePrint = () => {
    this.setState({ isPrinting: true }, () => {
        window.print();
        window.onafterprint(
            this.setState({ isPrinting: false })
        );
    });
};

or if you want to use hooks :

import {useEffect, useState} from 'react';

// Set isPrinting value and its setter function
const [isPrinting, setIsPrinting] = useState(false)

// Your handlePrint function reshaped for hooks
const handlePrint = () => setIsPrinting(true)

// Callback when isPrinting is updated to true
useEffect(() => {
    if (isPrinting) {
        window.print();
        window.onafterprint(setIsPrinting(false));
    }
}, [isPrinting])
Loïc Goyet
  • 720
  • 6
  • 6
  • Thank you for your detailed anser, it basically works, but when I closes the print dialog it's break the page on this error (`TypeError: window.onafterprint is not a function`) – user3378165 Apr 02 '19 at 09:43