react-pdf icon indicating copy to clipboard operation
react-pdf copied to clipboard

React Context not handled correctly (react-redux and react-intl)

Open ribx opened this issue 6 years ago • 20 comments

Describe the bug I am using react-redux and react-intl, which both use a provider component and react's context.

When I use a component, in this case on that is connected to the redux store, I get the following error:

Invariant Could not find "store" in the context of "Connect(Compoment)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Compoment) in connect options.

Found an issue with react-router, which could be important here:

If you use React Router, something like <Provider>{() => routes}</Provider> won’t work. Due to the way context works in React 0.13, it’s important that the <Provider> children are created inside that function. Just referencing an outside variable doesn’t do the trick. Instead of <Provider>{() => routes}</Provider>, write <Provider>{createRoutes}</Provider> where createRoutes() is a function that actually creates (and returns) the route configuration.

To Reproduce Steps to reproduce the behavior including code snippet (if applies):

  1. use a component that uses react's context within react-pdf components
  2. exception is thrown

(I have no time to create a minimal code snippet now, but could do so if someone needs it)

Expected behavior react-pdf should be able to render components, that rely on reacts context api

Desktop (please complete the following information):

  • OS: linux, Gentoo Linux
  • Browser: chrome and firefox
  • React-pdf/renderer version: v1.4.0

ribx avatar Mar 11 '19 09:03 ribx

A code snippet to replicate this would be great

diegomura avatar Mar 16 '19 07:03 diegomura

https://github.com/ribx/react-pdf-test

import React, {Component} from 'react'
import {createStore} from 'redux'
import ReactDOM from 'react-dom'
import {IntlProvider, FormattedMessage} from 'react-intl'
import {Provider as ReduxProvider, connect} from 'react-redux'
import {Document, Page, View, Text, PDFViewer} from '@react-pdf/renderer'


const store = createStore(state => state)

const Connected = connect(state => ({state}))(props => console.log('state', props.state) || props.children)

class App extends Component {
  render() {
    return (
      <div className="App">
        <PDFViewer>
          <Document>
            <Page>
              <View>
                <Text>
                  <FormattedMessage id="test">{s => s}</FormattedMessage>
                </Text>
              </View>
              <View>
                <Text>
                  <Connected>
                    Redux connected component Test
                  </Connected>
                </Text>
              </View>
            </Page>
          </Document>
        </PDFViewer>
      </div>
    )
  }
}

ReactDOM.render(
  <ReduxProvider store={store}>
    <IntlProvider locale="en" messages={{en: {id: "test", defaultMessage: "React PDF Test"}}}>
      <App/>
    </IntlProvider>
  </ReduxProvider>,
  document.getElementById('root'),
)

I think this is somehow connected to how the new context is working, but I have still problems understanding the concept of react-reconciler.

ribx avatar Mar 16 '19 11:03 ribx

Hi @diegomura, any idea how we can solve this problem. Using context API is a common case.

yjose avatar Jul 24 '19 17:07 yjose

Hi, just want to mention that I'm having also problems connecting this component to some context, either the Redux one, as well as other used in my current project.

Is there something we can do in order to help you?

cc @diegomura

sirgalleto avatar Sep 11 '19 01:09 sirgalleto

same issue here, can't inject react intl ... an alternative solution is to connect the parent component and pass data into props but still alternative.

zeabdelkhalek avatar Aug 19 '20 15:08 zeabdelkhalek

Having same issue on v1.6.12. Can't use react context API.

Nases avatar Dec 01 '20 05:12 Nases

I am having the similar issue.

Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

Some of my code for React PDF component are following.

import React from 'react';
import PropTypes from 'prop-types';
import {
  Document,
  Page,
  View,
  Text,
  Image,
  StyleSheet,
  Link
} from '@react-pdf/renderer';
import { useDispatch, useSelector } from 'src/store';

const InvoicePDF = ({ data }) => {
  const dispatch = useDispatch();
  const { invoice } = useSelector((state) => state.invoice);

  return (
    <Document>
      ...
    </Document>
  )
}

export default InvoicePDF;

package.json is the following.

    "@react-pdf/renderer": "^1.6.10",
    "@reduxjs/toolkit": "^1.4.0",

hotcakedev628 avatar Dec 18 '20 15:12 hotcakedev628

I am having the same issue. Any update regarding the same?

SrividyaKK avatar Jan 19 '21 17:01 SrividyaKK

I am having the same issue. Any update regarding the same?

@SrividyaKK It's still silent.

hotcakedev628 avatar Jan 21 '21 17:01 hotcakedev628

I see there is a version 2 branch. Has anyone tried this in yet? @SrividyaKK Or have the contributors add this yet? @diegomura

potofpie avatar Feb 03 '21 17:02 potofpie

I see there is a version 2 branch. Has anyone tried this in yet? @SrividyaKK Or have the contributors add this yet? @diegomura

Nope. Not me.

SrividyaKK avatar Feb 04 '21 11:02 SrividyaKK

I'm using the useContext hook and I always get the value as undefined, any workaround?

joaopedrocoelho avatar Apr 10 '21 08:04 joaopedrocoelho

Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

<PDFViewer>
    <LayoutReturn />
</PDFViewer>

...

LayoutReturn  : 
<Document>
    <Layouts layoutsBackgrounds={layoutsBackgrounds} setLayoutsBackgrounds={setLayoutsBackgrounds} />
</Document>

Layouts :
<Page size="A4" style={{ backgroundColor: "tomato" }}>
          {layoutsBackgrounds[1] ? (
            <View
              style={{ color: "white", textAlign: "center", margin: 30 }}
              onClick={() => {
                setNewBg(1);
              }}
            >
              <Text>Img</Text>
            </View>
          ) : (
            <View
              style={{ color: "black", textAlign: "center", margin: 30 }}
              onClick={() => {
                setNewBg(1);
              }}
            >
              <Text>No img</Text>
            </View>
          )}
        </Page>

Same issue

dluigirafael avatar Apr 13 '21 11:04 dluigirafael

image same with useContext

dluigirafael avatar Apr 14 '21 14:04 dluigirafael

This is an issue on the React side unfortunately. There's an open ticket for awhile now https://github.com/facebook/react/issues/17275. I'll try to reactivate that

diegomura avatar Jun 15 '21 14:06 diegomura

same issue here, can't inject react intl ... an alternative solution is to connect the parent component and pass data into props but still alternative.

I solved the issue by lift up the state to the parent component. thanks

walosha avatar Sep 06 '21 21:09 walosha

Hello, I am still having this issue has anyone come to a solution or work around?

mtullo27 avatar Oct 25 '22 13:10 mtullo27

I am not using react-redux or react-intl but here is what worked for a custom provider I wanted to access within a Document while using PDFViewer.

import { CustomProvider } from './CustomProvider';
import {
  PDFViewer as PDFViewerOriginal,
  PDFViewerProps,
} from '@react-pdf/renderer';

export const PDFViewer = ({ children, ...props }: PDFViewerProps) => {
  return (
    <PDFViewerOriginal {...props}>
      <CustomProvider>
        {/**
         * Due to a known issue with React, Contexts are not accessible by children of the 'react-pdf' PDFViewer.
         * Because 'react-pdf' is a custom renderer and current React limitations, we have to "bridge" contexts.
         * We need to subscribe to a context from within the PDFViewer and "bridge" the context by creating a Provider as a
         * child of the PDFViewer.
         *
         * For more info read this: @link https://github.com/diegomura/react-pdf/issues/522#issuecomment-861545047
         */}
        {children}
      </CustomProvider>
    </PDFViewerOriginal>
  );
};

Here is a possible solution if your provider is more complex and needs to access some data:

import { CustomProvider, CustomContext } from './CustomProvider';
import {
  PDFViewer as PDFViewerOriginal,
  PDFViewerProps,
} from '@react-pdf/renderer';
import { useContext } from 'react';

export const PDFViewer = ({ children, ...props }: PDFViewerProps) => {
  const customContext = use context(CustomContext);
  return (
    <PDFViewerOriginal {...props}>
      <CustomProvider value={customContext}>
        {children}
      </CustomProvider>
    </PDFViewerOriginal>
  );
};

You can read more about this solution here: https://github.com/facebook/react/issues/17275#issuecomment-550322731

markcnunes avatar Mar 18 '24 14:03 markcnunes

Any update on this as of yet?

tyler-caceres-nayya-tw avatar Jun 24 '25 18:06 tyler-caceres-nayya-tw

I am not using react-redux or react-intl but here is what worked for a custom provider I wanted to access within a Document while using PDFViewer.

import { CustomProvider } from './CustomProvider'; import { PDFViewer as PDFViewerOriginal, PDFViewerProps, } from '@react-pdf/renderer';

export const PDFViewer = ({ children, ...props }: PDFViewerProps) => { return ( <PDFViewerOriginal {...props}> <CustomProvider> {/** * Due to a known issue with React, Contexts are not accessible by children of the 'react-pdf' PDFViewer. * Because 'react-pdf' is a custom renderer and current React limitations, we have to "bridge" contexts. * We need to subscribe to a context from within the PDFViewer and "bridge" the context by creating a Provider as a * child of the PDFViewer. * * For more info read this: @link https://github.com/diegomura/react-pdf/issues/522#issuecomment-861545047 */} {children} </CustomProvider> </PDFViewerOriginal> ); }; Here is a possible solution if your provider is more complex and needs to access some data:

import { CustomProvider, CustomContext } from './CustomProvider'; import { PDFViewer as PDFViewerOriginal, PDFViewerProps, } from '@react-pdf/renderer'; import { useContext } from 'react';

export const PDFViewer = ({ children, ...props }: PDFViewerProps) => { const customContext = use context(CustomContext); return ( <PDFViewerOriginal {...props}> <CustomProvider value={customContext}> {children} </CustomProvider> </PDFViewerOriginal> ); }; You can read more about this solution here: facebook/react#17275 (comment)

This works with PDFViewer but not with PDFDownloadLink which expects its content to go in the document prop.

carlos-vitallink360 avatar Oct 27 '25 02:10 carlos-vitallink360