How can I inject props into a Provider component? - reactjs

I have an entry point file that exports a Provider wrapping a component. For the life of me I can't get props into it. Any ideas?
entry point file that I call sometime in my app. It is a seperate file, and lives outside my app. Thus, the wrapping of a Provider.
const App2 =
<Provider store={myOtherStore}>
<Application />
</Provider>
;
export default App2;
I tried this and some other ways.. but no luck. The entry component and any other component tied to Application does not see any new props other than "actions and state"
const App2 = (props) =>
return (
<Provider store={myOtherStore}>
<Application data={props} />
</Provider>
)
;
export default App2;
basically, in my working file I'd call that above like so:
const newApp = React.cloneElement(newApplication, {
productID
});
Edit: In the Application component (the one the Provider surrounds), I am using connect on that and bringing in the actions/state. It is just in this component "Application" I do not have access to the props I am trying to pass in. I just see {actions:{}, state: {}}

Related

React app does not have consistent provider for redux in children

I am building a React app with sagas for Redux and next.js. The main app.js file is handled as follows:
import { wrapper } from '../state/store';
const App = ({ Component, pageProps }) => (
<Layout>
<Component {...pageProps} />
</Layout>
);
export default wrapper.withRedux(App);
I can call and write to the state using dispatch for the pages in this app and some of their direct children. However, with others I get an error that is always something like ...
Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
I don't understand how the parent page and some children are fine, but others have problems. It seems like components must be registered somewhere to be in the provider as every new component that I add has problems.
If the app is not wrapped in a provider, why does it work in the main page and some children? If it is wrapped, why aren't some components seeing that?

Using React-Redux: How to Access Variables in the Store, within Nested Components

I am using React-Redux, but I am not able to figure out how to access a variable in the Redux store inside of my nested components.
How can I share a variable between components, using React-Redux?
For example:
I have an 'index.js' file and 30 nested components. Managing these components becomes difficult after a while.
I have a 'C1.js' component. Let's just say I wrote this code in it.
function Reducer(state = 'example' , action) {
return state;
}
const store = createStore(Reducer)
index.js file:
<Provider store = {store}>
<App/>, document.getElementById('root')
</Provider>
How do I pass the 'store' variable to the 'C1.js' component to the index.js file?
Thanks...
You need to use something called "Connect" to connect your various components to the provider.
In the file that contains your C1.js component:
import {connect} from 'react-redux'
const MyComponent = () => {
let someData = props.someData
return(
//all of your JSX for your component here
)
}
const mapState = state => {
return {
someData: state.someData
}
}
export default connect(mapState)(MyComponent)
In the code above, notice the mapStateFunction. Connect is hooking that up with the Provider, and the state that is on the Provider. So that is where you are able to link whatever properties are on your Provider (React-Redux) state with this particular data.
Now, in your component, you will now have prop.someData
-
In the index file, you have your Provider in the wrong place, you need to change your code to this:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
See, the difference there? The is the React Element (and all of its children that you are asking React to render to the DOM). It is the first parameter of the ReactDOM.render function.
The second parameter to the ReactDom.render function is the element in the DOM where you want it to put all of your React elements.
You did not configure well redux and react. You need to go over the doc of redux to setup correctly. Should get working after that.

React Context: Provide instance of an Api Manager

I want to keep a single instance of a third party api manager class in my react app.
I'm trying to achieve this using using the React Context Api like this:
Api Context:
import * as React from "react";
import { ApiManager } from "../ApiManager";
const defaultValue = new ApiManager()
const ApiContext = React.createContext(defaultValue);
const ApiProvider = ApiContext.Provider;
const ApiConsumer = ApiContext.Consumer;
const withApi = (enhanced) => {
return (
<ApiConsumer>
{api => enhanced }
</ApiConsumer>
)
}
export default ApiContext;
export {ApiContext, ApiProvider, ApiConsumer, withApi};
in my client I would do something like this:
const api = new ApiManager({
host: apiHost,
baseUrl: apiBaseUrl,
key: apKey,
sessionToken: persistedSessionToken
});
ReactDOM.hydrate(
<Provider store={store}>
<PersistGate loading={<h1>loading...</h1>} persistor={persistor}>
<BrowserRouter>
<ApiProvider value={api}>
<Main />
</ApiProvider>>
</BrowserRouter>
</PersistGate>
</Provider>, document.querySelector('#app')
);
I have 3 questions:
Does this even make sense?
How would I set the sessionToken if it gets renewed by the user?
How do I rehydrate the api instance after a page reload using redux-persist?
If you just want to use one instance of ApiManager then I don't think you need Context API. Just create an instance and export/import it in files where you need it.
You can wrap that third party api manager to add some methods you need like setSessionToken to set/update sessionToken when app loads or when user receives/refreshes it.
Why bother with redux-presist in this case (even if you decide to use Context API) if Redux is not involved. If you want to store sessionToken so store it in localStorage or any other storage you want, it has simple API to set/retrieve values, you don't need to use a library for it.

Mocking Redux store when testing React components?

I'm using React and Redux. I have a component which loads ChildComponent and depending on Redux's state will also load MainComponent
const ChooseIndex = ({ appInitMount }) => {
return (
<>
<ChildComponent />
{!appInitMount && <MainComponent />}
</>
);
};
const mapStateToProps = ({ main }) => {
return {
appInitMount: main.appInitMount
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChooseIndex);
I'm trying to write a test to check that ChildComponent is loaded:
import React from "react";
import { render } from "react-testing-library";
import ChooseIndex from "../choose-index";
test("ChooseIndex should call ChildComponent", () => {
const wrapper = render(
<ChooseIndex />
);
});
I get this error:
Error: Uncaught [Invariant Violation: Could not find "store" in either
the context or props of "Connect(ChooseIndex)". Either wrap the root
component in a , or explicitly pass "store" as a prop to
"Connect(ChooseIndex)".]
Should I mock Redux by passing an object literal to ChooseIndex? Or should I create a Redux store (as my real application does) for every test?
Try to render your component like this:
render(
<Provider store={store}>
<ChooseIndex />
</Provider>
)
And pass the actual store you use in your app. In this way, you're testing the real logic that you'll use in production. You also don't care what actions get dispatched and what's in the state. You look at what gets rendered and interact with the UI—which is what matters in the end.
Separating the component from Redux and testing the two in isolation is against the whole point of react-testing-library. You want to test your app as a real user would.
If you check out the writing tests section of the redux docs, there is an example of testing a connected component.
when you import it [A redux connected component], you're actually holding the wrapper component returned by connect(), and not the App component itself. If you want to test its interaction with Redux, this is good news: you can wrap it in a with a store created specifically for this unit test. But sometimes you want to test just the rendering of the component, without a Redux store.
In order to be able to test the App component itself without having to deal with the decorator, we recommend you to also export the undecorated component
As with most unit tests, you really want to be testing your components, and not that redux is working correctly. So the solution for you is to export both the component and the connected component, while only testing the component itself, and providing whatever props redux is passing to your component.
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)

How to access store in second component in react-redux

I have a single component App.js where I trying to save state using redux. In index.js where I set store for only <App /> component.
index.js
let store = createStore(scoreReducer);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById("root")
);
registerServiceWorker();
I have this method in App.js to map state to props which is available inside App.js.
App.js
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
Everything is well so far, now I am not sure how to access { this.props.score} in another component ?
What changes I need to do in index.js and second component if I want to access {this.props.score} in another component ?
When you are using Provider any component that is children of the Provider Higher Order Component can access the store properties though the use of connect function.
So you can add the following in any component that is a child of Provider and access the score prop
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
export default connect(mapStateToProps)(MyComponent)
However if this other component is a direct child of App component then you can also pass the score prop as a prop to this component from App like
<MyComponent score={this.props.score}/>
Provider component sets the context for all its children, providing the store in it. when you use the High Order Component(HOC) connect you can wrap any component and access the store through the provided mapStateToProps and mapStateToProps no matter how nested they are. You can also access the store using context context.store but this is not recommended. Using map functions and connect, similar to what you have with your App component, is the best approach.

Resources