admin管理员组

文章数量:1357302

I have a react app which generates images on the front end dynamically using Plotly.js. I'd like to add image sharing functionality. I am trying to use react-share for this. Social platforms require image URL for image sharing and do not support images in base64 encoding or alike. Backend was implemented so it can receive images in base64, store in the database and return URL to the image, which is then used for sharing with react-share.

As the image is generated dynamically (it changes each time user resizes the chart, for instance), everything should be done when user clicks on Share icon.

So after the user has clicked on the Share icon, the image generated on the front end should be saved to back end

let imgURI;

  const handleClick = () => {
    Plotly.toImage('chartContainer', {
      format: 'png',
      width: 1000,
      height: 600
    })
      .then(dataUrl => api.post('/image/base64ToPng', { image: dataUrl })
        .then(
          (response) => {
            imgURI = response.data.imgURI;
          },
          failure => console.error(failure)
        ));
  };

after the response is received, passed down to the sharing ponent like this

<FacebookShareButton
            url={imgURI}
          >
     <FacebookIcon/>
</FacebookShareButton>

The code sample is not asynchronous, so the image URI is not passed to the sharing ponent, therefore sharing does not work. I tried to pass the prop down using conditional depending on whether it's defined or not and did not e up with a solution. I also looked up some issues in react-share repo that dealt with async urls, but seems like none of them deals with the dynamic image sharing on click.

I'd very appreciate a hint on how to plete this task.

I have a react app which generates images on the front end dynamically using Plotly.js. I'd like to add image sharing functionality. I am trying to use react-share for this. Social platforms require image URL for image sharing and do not support images in base64 encoding or alike. Backend was implemented so it can receive images in base64, store in the database and return URL to the image, which is then used for sharing with react-share.

As the image is generated dynamically (it changes each time user resizes the chart, for instance), everything should be done when user clicks on Share icon.

So after the user has clicked on the Share icon, the image generated on the front end should be saved to back end

let imgURI;

  const handleClick = () => {
    Plotly.toImage('chartContainer', {
      format: 'png',
      width: 1000,
      height: 600
    })
      .then(dataUrl => api.post('/image/base64ToPng', { image: dataUrl })
        .then(
          (response) => {
            imgURI = response.data.imgURI;
          },
          failure => console.error(failure)
        ));
  };

after the response is received, passed down to the sharing ponent like this

<FacebookShareButton
            url={imgURI}
          >
     <FacebookIcon/>
</FacebookShareButton>

The code sample is not asynchronous, so the image URI is not passed to the sharing ponent, therefore sharing does not work. I tried to pass the prop down using conditional depending on whether it's defined or not and did not e up with a solution. I also looked up some issues in react-share repo that dealt with async urls, but seems like none of them deals with the dynamic image sharing on click.

I'd very appreciate a hint on how to plete this task.

Share Improve this question asked Nov 16, 2021 at 21:43 PoliakoffPoliakoff 1,6822 gold badges22 silver badges40 bronze badges 1
  • Perhaps you could leverage useState (reactjs/docs/hooks-state.html)? const [imgURI, setImgURI] = useState(); And then inside your handleClick you do setImgURI(response.data.imgURI) – Tor Raswill Commented Nov 16, 2021 at 22:19
Add a ment  | 

3 Answers 3

Reset to default 2 +50

This is serious hack territory, and the whole thing would be a lot simpler if this PR had been pleted.

However, the code below should work (see codesandbox).

The key steps are:

  1. Have a bit of state that keeps track of whether you have a url from the service or not.
  2. When this state is "none", disable the facebook button's default behavior (i.e. openShareDialogOnClick = false)
  3. Add an onClick handler to the facebook button that asynchronously fetches the url and sets the state (triggering a re-render)
  4. Use an effect + ref so that when the url is set to something real, you manually call the click event on the button (which now has a real address in its url prop), and then re-sets the url to "none"
import { useEffect, useRef, useState } from "react";
import { FacebookIcon, FacebookShareButton } from "react-share";

async function getUrFromService(): Promise<string> {
  // The real implementation would make a network call here.
  await new Promise((resolve) => setTimeout(resolve, 1000));
  return "https://via.placeholder./150";
}

export default function App() {
  const shareButton = useRef<HTMLButtonElement>(null);
  const [url, setUrl] = useState<string>("none"); // Unfortunately, we have to have a dummy string here, or FacebookShareButton will blow up.

  // Provide an onClick handler that asyncronously fetches the url and sets it in the state.
  const onClick = async () => {
    // Be sure to check for the "none" state, so we don't trigger an infinite loop.
    if (url === "none") {
      const newUrl = await getUrFromService();
      setUrl(newUrl);
    }
  };

  // Whenever "url" changes and we re-render, we manually fire the click event on the button, and then re-set the url.
  useEffect(() => {
    if (url !== "none") {
      shareButton.current?.click();
      setUrl("none");
    }
  }, [url, shareButton]);

  return (
    <FacebookShareButton
      ref={shareButton}
      // Disable calling the dialog if we don't have a url yet.
      openShareDialogOnClick={url !== "none"}
      url={url}
      onClick={onClick}
    >
      <FacebookIcon />
    </FacebookShareButton>
  );
}

Use navigator.share api if possible.

Having, said that you could write a wrapper that manages loading state and disables icon of ponent.

Create a async share ponent

//AsyncShareLoader.jsx
const AsyncShareLoader = ({ url, children }) => {
  const loading = !url;
  return (
    <div style={{ filter: `grayscale(${loading ? "100%" : "0%"}` }}>
      {React.Children.map(children, (child) =>
        React.cloneElement(child, {
          disabled: loading,
          url: loading ? "none" : url,
          openShareDialogOnClick: !loading
        })
      )}
    </div>
  );
};

Now, use this as a wrapper for your actual react-share icons. Something like this

const Share = () => {
  const [url, setUrl] = useState()

  useEffect(() => {
    fetch('/imgUrl').then(getUrlFromRes).then(setUrl)
  }, [])

  return (
    <AsyncShareLoader url={url}>
      <FacebookShareButton>
        <FacebookIcon />
      </FacebookShareButton>
    </AsyncShareLoader>    
  );
}

Extending this techinique you could manually steal click as Andrew suggested

const getBase64ImageFromUrl = url => fetch(url) .then(response => response.blob()) .then( blob => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; blob = new Blob([blob], {type: 'image/png'}); reader.readAsDataURL(blob); }), );

const shareData = async () => {

await getBase64ImageFromUrl('https://url of image').then(base64 => {
  const shareOption = {
    title: App Link,
    url: base64,
    message:
      'Download app now: ,AppLink:""',
  };
  try {
    const shareResponse = Share.open(shareOption);
    console.log('share response ,', shareResponse);
  } catch (error) {
     alert(error.message);
  }
});

};

本文标签: javascriptHow to share a dynamically generated image from react with reactshareStack Overflow