Is it enough to use Provider one time? - javascript

In react-native when we use redux module we use createStore from 'redux'. And My question: is it enough to use <Provider/> one time which makes the Redux store available to the rest of our app.
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
const rootElement = document.getElementById('root')
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
Or we must also add <Provider/> somewhere?

Once you create a store with redux with the reducers for example just like below:
const store = createStore(reducers, applyMiddleware(thunk));
Then you need to use <Provider> wrapper only once:
ReactDOM.render(
<Provider store={store}>
<App />
<Provider>, document.getElementById('root')
);
Later if you want to access any of the object from the store you need to use mapStateToProps in your components. If any modification is needed you need to create actions and dispatch them with mapDispatchToProps.
From the the documentation:
The option we recommend is to use a special React Redux component called to magically make the store available to all container components in the application without passing it explicitly. You only need to use it once when you render the root component.
Hope this helps.

Yep, you only need to use Provider once to wrap your whole application.

The question looks interesting. You need to understand how the store is available to every component wrapped inside the <Provider>.
When you wrap any React component inside <Provider store={store}> every component will have access to something called context, through which it is possible to share the data even it's not an immediate child.
Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree. https://reactjs.org/docs/context.html
As it states that context can be used in any child component without explicitly passing a prop.
The <Provider> exactly works the same way. And when you use the redux the <Provider> sets the context. And you need to use the connect() from the react-redux it uses the context set by the <Provider> to pass properties to your component wrapped in connect() HOC.
Hence you need to use the <Provider> only once in your application.

Related

Will Redux inside a React component from npm conflict with the container's Redux?

I want to encapsulate a npm React component, and I want to use Redux to manage state inside this React component.
When another React project imports my component, will this React project's redux instance have conflict with my React component?
Here is the example:
The component will look like:
// File Explorer Component, will be build into a npm package
import React from "react";
import { Provider } from "react-redux";
import { store } from "./store";
function FileExplorer({source}) {
<Provider store={store}>
{/* there will be complicated business logic inside */}
</Provider>;
}
Then, I create another React project, and import the FileExplorer component from npm
index.jsx
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { store } from "./store"; // this store is a different one
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
rootElement
);
App.jsx
import FileExplorer from 'file-explorer';
export default function App() {
return (
<div className="App">
<FileExplorer source={'https://example.com/data/v1/files'} />
</div>
);
}
A couple thoughts here.
The first is yes, this would absolutely cause a clash if the components nested inside of <FileExplorer> also need to access the app-wide Redux store, because by default all Redux-connected React components access the same store instance via React context.
It is technically possible to pass an alternate context to <Provider> and create customized versions of useSelector/useDispatch that read the store from that context:
https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context
although it looks like we don't actually document the API for creating the context-customized versions of the selector hooks atm.
React-Redux should be exporting createSelectorHook and createDispatchHooks functions, which you can see in use here:
https://github.com/reduxjs/react-redux/blob/v7.2.6/src/hooks/useSelector.js#L166
Should be used as:
const useCustomContextSelector = createSelectorHook(MyCustomContext);
All that said: I'd still lean towards tracking the state in a useReducer hook as the default rather than an actual separate Redux store.
Note that you can use RTK's createSlice to create a reducer specifically for use in a useReducer hook - reducer functions are just functions and work fine in either place.

Persist data between two pages with Next.js

I would like to refactor my Next.js webapp to have different pages handle different screens. Currently, I have this component holding several states to know in which screen I'm in. In the jsx section, I'm using {value && ... } to render the right component.
But I feel this is not good design, and won't be maintainable when adding more and more screens.
I would also like to avoid Redux as it is overkill for my project.
I was thinking about persisting data in cookies so I can retrieve them with getInitialProps in every component when rendering a new page, but is there a more elegant way?
I've read about tweaking the _app.js but I'm not sure to understand the consequences of doing so, and how it could help me..
Any suggestion?
When multiple of your pages need to make use of same data, you can make use of Context to store the result. It a good way to make a centralized storage without using complex and more self sufficient libraries like redux
You can implement context inside of _app.js file which must reside inside your root folder. This way next.js treats it as a root wrapper and you would just need to use 1 instance of Context
contexts/appContext
import React from 'react';
const AppContext = React.createContext();
export const AppProvider = AppContext.Provider;
export const AppConsumer = AppContext.Consumer;
export default AppContext;
_app.js
import React from 'react'
import App from 'next/app'
import AppProvider from '../contexts/appContext';
class MyApp extends App {
state={
data:[]
}
render() {
const { Component, pageProps } = this.props;
// You can implement logic in this component to fetch data and update state
return (
<div>
<AppProvider value={this.state.data}> // pass on value to context
<Component {...pageProps} />
</AppProvider>
</div>
)
}
}
export default MyApp
Now further each component can make use of context value by using AppConsumer or using useContext if you use hooks
Please read more about how to use Context here

Passing a function from Context to a component to update state in context

I'm having issues passing a function from my context that sets the state inside the context to a component. The function is coming back undefined when I console log it, but everything else in the context shows up when I console log them. It's supposed to update the state of "display" inside my context from the Header component. I've tried finding solutions on stack overflow but nothing has worked so far.
https://codesandbox.io/s/github/joeyjr95/max-kaplan-portfolio/tree/master/?fontsize=14&hidenavigation=1&theme=dark
There was a tiny syntax error in the index. This is how the index should look. Take a look at the PortProvider.
ReactDOM.render(
<BrowserRouter>
<PortProvider>
<App />
</PortProvider>
</BrowserRouter>,
document.getElementById("root"),
console.log(PortProvider)
);
the original:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App/App";
import { BrowserRouter } from "react-router-dom";
import { PortProvider } from "./Context/Context";
ReactDOM.render(
<BrowserRouter>
<PortProvider/>
<App />
<PortProvider/>
</BrowserRouter>,
document.getElementById("root"),
console.log(PortProvider)
);
A few tiny issues, Typo in index.js self-closing <Provider/> and Useless default value, as it is replaced by state. Changed the menu <a> to <div>. Optimized setDisplay, also passed it as toggle then used where required.
<PortContext.Provider value={{ ...this.state, toggle: this.setDisplay }}>
{this.props.children}
</PortContext.Provider>
Fixed Code Sandbox

ReactJS - Accessing data on Different Pages

I am trying to access data gathered from a user on one page and use it on another page. I have tried following these articles:
https://travishorn.com/passing-data-between-classes-components-in-react-4f8fea489f80
https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
https://codeburst.io/react-js-pass-data-from-components-8965d7892ca2
I have not been able to get it to work. this.props.{variableName}keeps returning as undefined. My code is as follows.
The following is the Home Page:
import React, { Component } from 'react';
import {Button} from 'reactstrap';
import { browserHistory } from 'react-router';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
working: "",
};
}
WorkReqNav(){
this.setState=({working: "WORKING"});
browserHistory.push("/WorkReq");
}
render() {
return (
<div>
<Button size="lg" onClick={this.WorkReqNav.bind(this)} type='button'>HIT IT!</Button>
</div>
);
}
}
export default HomeScreen;
The following is the workReq screen:
import React, { Component } from 'react';
import {Button} from 'reactstrap';
class WorkReq extends Component {
constructor(props) {
super(props);
}
workCheck(){
var working = this.props.working;
alert(working);
}
render() {
return (
<div>
<Button size="lg" onClick={this.workCheck.bind(this)} type='button'>HIT IT!</Button>
</div>
);
}
}
export default WorkReq;
If you need anything more, please let me know. i am really new to React and this is my first time attempting anything like this.
welcome to the React world. I bet you'll love it when you gradually get familiar with cool stuff that you can do with React. Just be patient and keep practicing.
So the first suggestion I would make is that, like any other javascript environment, React also evolves very quickly. So although basic principles are the same, when you follow a new article on one hand, on the other hand you can check if the libraries or methodologies that are demonstrated are up to date.
Fasten your belts and let's do a quick review based on your question and libraries that I see you used in your example.
In terms of router, I see that you directly export things from react-router
When we check the npm page (https://www.npmjs.com/package/react-router) of react-router they make the following suggestion
If you are writing an application that will run in the browser, you
should instead install react-router-dom
Which is the following package https://www.npmjs.com/package/react-router-dom
You can get more details and find more tutorials in order to improve your skills by checking their official page https://reacttraining.com/react-router/web/guides/philosophy
Let's take a look at the code snippet sasha romanov provided that's based on react-router-dom syntax
with react-router-dom when you define a route with following syntax
<Route path="/" component={HomePage} exact />
react-router-dom automatically passes match, location, and history props to HomePage component. So when you console.log() these props, you should be able to display somethings on your console. And once you have access to history props, instead of browserHistory, you can use this.props.history.push("/some-route") for redirections.
Let's take a look at the part related to withRouter. In the example above, we could use history because HomePage component was passed directly to the Router component that we extract from react-router-dom. However, in real life, there might be cases in which you want to use history props in a component that's not passed to the Router but let's say just a reusable button component. For these cases, react-router-dom provides a Higher Order Component called withRouter
A Higher Order Component is (from React's official documentation)
https://reactjs.org/docs/higher-order-components.html
Concretely, a higher-order component is a function that takes a
component and returns a new component.
So basically, whenever you wrap any component with withRouter such as export default withRouter(MyWrappedReusableComponent), in your reusable component, you will have access to the props history, location, pathname
That said, my first impression regarding to your problem does not seem to be related to router logic but rather exchanging data between components.
In your original question, you mentioned that
I am trying to access data gathered from a user on one page and use it on another page
There are a couple of cases/ways to approach this issue
1) If these two components are completely irrelevant, you can use state management system such as Redux, mobx or you can use React's context API https://reactjs.org/docs/context.html. HOWEVER, since you are new to React, I would suggest not tackle with these right know till you are comfortable with the basic flow. Because at some point trying to implement a flow with a lot of libraries etc. is quite overwhelming. Believe me, I tried when I was also new to React and I was really close to break my computer after opening my 100th browser tab to look for another method from another library
2) You can implement a simple parent-child relationship to pass data between components. Let me explain what I mean by using references from your code snippet.
I believe you want to update working which is a state in your HomeScreen and you want to pass and use this updated value in your WorkReq component.
If we ignore all the routing logic and decide to go without routes, what you need to do is the following
import React, { Component } from 'react';
import {Button} from 'reactstrap';
import { browserHistory } from 'react-router';
import WorkReqComponent from 'path/to/WorkReqDirectory';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
working: "WORKING",
};
}
render() {
return (
<div>
<WorkReqComponent working={this.state.working} />
</div>
);
}
}
By this way, when you log this.props.working; in your WorkReqComponent you should be able to display the data that you passed. You can refer to this as passing data from parent to child.
I checked the articles you listed. They also seem to explain data transfer between parent to child, child to parent or between siblings.
In your case, what you really need to implement can be categorized as between siblings
I prepared a sample for you with react-router-dom to demonstrate one possible structure which might yield your expected outcome.
https://codesandbox.io/s/ojp2y0xxo6
In this example, the state is defined inside of the parent component called App. Also state update logic is also defined inside of the parent component. HomeScreen and WorkReq components are the children of App thus they are siblings. So, in order to transfer data between siblings, one of them was given the task of updating parent's state via passing state update logic to this component. The other one has the task of displaying parent's state's value.
At this point, since you are new and in order not to overwhelm yourself, you can experiment with parent-child-sibling data transfer topic. Once you are getting comfortable with the implementation and the logic, you can gradually start taking a look at React's context api and Redux/mobx.
Let me know if you have any questions regarding to the sample I provided
You can use react-router-dom lib and from seeing your code i think in parent component (app.js) you defined route for each child component you'd like to access
like this example here:
import { BrowserRouter, Route, Switch } from 'react-router-dom';
<BrowserRouter>
<div>
<Switch>
<Route path="/" component={HomePage} exact />
<Route path="/homescreen" component={HomeScreen} />
<Route path="/workreq" render={(props) => <WorkReq {...props} />} /> // here you can pass the props by calling render
<Route component={NoMatch} />
</Switch>
</div>
</BrowserRouter>
and then if you want to change route you can just call this.props.history.push('/workreq')
and if you didn't include route for the component in <BrowserRouter />
in the component that it's not included you can import withRouter and export like this withRouter(HomeScreen) and now you can access router props
if this isn't the answer you are looking please inform me to update my answer, i hope this can help

React Router Route Params in Redux

Using redux and react router, I would like to access a route parameter on a component other than the one specified by the route.
I've got react-router, redux, and react-router-redux set up as follows and I would like to access the reportId parameter from my redux store.
const history = syncHistoryWithStore(browserHistory, store);
const store = createStore(reducers);
const routes = (
<Router history={history}>
<Route path="/" component={MainLayout} >
<IndexRoute component={Home} />
<Route path="reports">
<Route path=":reportId" component={ReportViewerContainer} />
</Route>
</Route>
</Router>
);
ReactDOM.render(
<Provider store={store}>{router}</Provider>,
document.getElementById('root')
);
I've tried hooking up react-redux-router to store the route state, but I've found that there is nothing useful stored inside of redux besides the full path to the active route. This has left me with the options of either parsing out the reportId from the path in redux, or creating my own custom action in redux and updating it with the componentWillReceiveProps lifecycle method inside of my ReportViewerContainer component. There must be a better way?
If you want to access the router parameter inside the component, the best way to achieve this by using withRouter
https://egghead.io/lessons/javascript-redux-using-withrouter-to-inject-the-params-into-connected-components
You will find the better understanding with above example.
If you want to access the router in the redux store, there's a HOC called withRouter (docs). If I remember correctly, this will pass any router state to your component as props.
The only change you need to make to your component would be this:
import { withRouter } from 'react-router'
export default withRouter(ReportViewerContainer);
So I think you can use context to fetch the location that will be passed through the Router. ( Refer that link on how it works.) This should give you a solid direction to work on.
This can also help you down the lane to work with internationalization as well.
class MyComponent extends React.Component {
static contextTypes = {
router: React.PropTypes.object
};
render(){
// By declaring context type here, and childContextTypes
// on the parent along with a function with how to get it,
// React will traverse up and look for the `router` context.
// It will then inject it into `this.context`, making it
// available here.
}
}

Categories

Resources