Making my Toast component available to child components - javascript

I am trying to find a way to have my 3rd party Toast component, available in child components. In my example, I have it working with direct children of the app.js. But I'm not sure how to get any grand children to make use of it.
I have a repo at https://github.com/CraigInBrisbane/ReactLearning if that helps. (Never done open source stuff, so not sure if you can edit)
I am trying to use this component:
(https://github.com/jossmac/react-toast-notifications)
In my app.js importing the packages, and then loading the child components with:
<Route exact path="/accounts" component={withToastManager(Accounts)} />
Then (somehow) my child component has access to the toastManager:
const { toastManager } = this.props;
And I can show toast notifications with:
toastManager.add('Login Success', {
appearance: 'success',
autoDismiss: true,
pauseOnHover: false,
});
However, for my NavBar... I have a bit of a structure.
Header is my component that I call from app.js... Header it's self doesn't use the Toast. But it has a child called NavBar, which has a Logout button. When I click Logout, I want to show toast.
But it has no idea what ToastManager is.
I am trying this:
handleLogout() {
const { toastManager } = this.props;
const {pathname} = this.props.location;
this.context.changeAuthenticated(false);
if(pathname !== "/") {
this.props.history.push("/");
}
toastManager.add('You\re now logged out', {
appearance: 'success',
autoDismiss: true,
pauseOnHover: false,
});
}
But toastManager is not an item available to me.
Is there a way I can make any component that will use Toast notifications, be aware of them? I think I need to wrap my navbar in withToastManager, but then all my siblings need to have a reference to ToastManager.
Any guidance would be great.

You should take a look at react's Higher Order Component (HOC) documentation, but basically you want to wrap each component individually that needs the withToastManager prop(s) injected.
In your navbar.js file you'll want to import the withToastManager from 'react-toast-notifications', and wrap the export:
import { toastManager } from 'react-toast-notifications';
...
export default withToastManager(
withRouter(Navbar)
);
This will add the toast manager as a prop that you can access in the component via this.props.toastManager.
A side note, typically you'll also want to use this same pattern of wrapping the export of components that need props/functionality that the HOCs provide instead of elsewhere in the app, like in your router in app.js.

Related

Designing persistent layouts in Next.js

I'm going through this article and I'm trying to figure out how the persistence is supposed to occur in Option 4. From what I can tell, you'd need to redefine the .getLayout for every page. I'm not sure how the logic for nesting is incorporated into further urls.
Here's the code from the article
// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'
const AccountSettingsBasicInformation = () => <div>{/* ... */}</div>
AccountSettingsBasicInformation.getLayout = page => (
<SiteLayout>
<AccountSettingsLayout>{page}</AccountSettingsLayout>
</SiteLayout>
)
export default AccountSettingsBasicInformation
// /pages/_app.js
import React from 'react'
import App from 'next/app'
class MyApp extends App {
render() {
const { Component, pageProps, router } = this.props
const getLayout = Component.getLayout || (page => page)
return getLayout(<Component {...pageProps}></Component>)
}
}
export default MyApp
For example, say AccountSettingsBasicInformation.getLayout is /settings/, how would I use this template to produce something at /settings/username
P.S. If someone has done something in the past they'd recommend over this, I'm open to ideas.
Yes, you have to redefine the getLayout function to every page. As long as the SiteLayout component stays “unchanged” (eg.no props change) the rendered content in that layout component (not the page content itself) stays persistent. This is because React wont rerender that component.
I used Adam’s article when I was building next.js lib for handlin modal routes. You can check the example folder where you can see I am defining the getLayout property on every page which should be rendered with layout.
Example: https://github.com/svobik7/next-bodies/tree/master/example

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

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

Onsen UI implement login with React

While migrating our Onsen v1 app (based on AngularJS1) to Onsen v2 with React, I bumped into a little problem: implementing logic regarding user authentication (redirecting to login screen if there are no "saved credentials")
Here's how my app is structured...
Entry point is the App.js file, which looks like this:
import React from 'react';
import {Navigator} from 'react-onsenui';
import Home from './Home';
import Login from './Login';
class App extends React.Component {
renderPage(route, navigator) {
route.props = route.props || {};
route.props.key = route.comp.displayName;
route.props.navigator = navigator;
return React.createElement(route.comp, route.props);
}
render() {
return (
<Navigator
initialRoute={{comp: Home}}
renderPage={this.renderPage}
/>
);
}
}
export default App;
You can see that I have my references for both 'Home' and 'Login' components, however, I want to decide which component to render.
I tried the following: use 'Home' as the initialRoute, and decide in the Home components constructor if we need to go to the Login page.
constructor(props) {
super(props);
this.state = {
isOpen: false
};
const authenticated = GetAuthenticated();
if(!authenticated) {
this.props.navigator.pushPage({comp: Login});
}
}
This simply did not work, the Login page was never shown.
So basically, what I would like to do is to look in the localStorage for user credentials (or any other place for storing these kind of information), and based on this decide whether to load up the 'Home' page or the 'Login' page.
If GetAuthenticated() is a fetch call you need to remember it acts asynchronously. I'd recommend a loading section whilst the fetch is pending and then a decision on the result of the method.
This link is a good example of how you can use componentDidMount to demonstrate pending async calls. Once that is implemented you can go on to do your switch.
DO NOT, and I can't stress this enough, store user credentials in localStorage ever. Never ever.

Use react without a router component

If I want to make a web application using reactjs that is not a single page.
Should I compile all the react code into a single file and load it on all pages of the application, then use the function that I expose to render the necessary components?
Example of an html file
<div id="Clock" data-react="Clock"></div>
<div id="HelloWorld" data-react="HelloWorld"></div>
example of index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Clock from './Clock';
import HelloWorld from './HelloWorld';
import OtherComponent from './OtherComponent';
const APPS = {
Clock,
HelloWorld,
OtherComponent
};
const MyReactRender = react => {
let component = react.getAttribute('data-react');
let App = APPS[component];
if(App != undefined) {
ReactDOM.render(<App />, document.getElementById(component));
}
}
document.querySelectorAll('[data-react]').forEach(MyReactRender);
I'd see two ways, of increasing quality and difficulty. In both cases, you use good old anchors elements to redirect the page to a url, to which different templates correspond.
Manually check for the existence of divs id's
In this case, each template includes the same javascript bundle that contains everything in the app and a single element with an id corresponding to the specific component. The idea is to check wether or not an element is present in the page, then activate its corresponding react component if it is.
if (document.getElementById('component-root')) {
ReactDOM.render(<Component />, document.getElementById('component-root'));
}
On the up side, it's quite easily implemented. On the down side, the bundle will always get bigger and bigger, and the list of ifs grows each time you add a new "page".
Separate your modules in actual bundles
Different bundle managers exist, but I'd recommend using Webpack to create multiple bundles that contain only specific part of your application. Then, each template contains only the corresponding div element, as well as that specific bundle.
<head><script src="/js/clock.js"></head>
<body><div id="root-clock"></div></body>
<head><script src="/js/otherComponent.js"></head>
<body><div id="root-other-component"></div></body>
How to package multiple bundles with webpack is out of the scope of this answer, but look here.
I've tried making a react application without a router. I used ternary operators to switch from component to component.
// App Component
class App extends Component {
constructor(props) {
super(props)
this.state = {
inClockComponent: true,
inHelloWorldComponent: false,
inOtherComponent: false
}
}
render() {
const {inClockComponent, inHelloWorldComponent, inOtherComponent} = this.state
return (
<div>
{
inClockComponent
? <Clock> : inHelloWorldComponent
? <HelloWorld> : inOtherComponent ? <OtherComponent> :
<div>No Component Here</div>
}
</div>
}
You could pass a function from the App component that would change the display state to each child component of App
Example
// in App Component
showHelloWorldComponent() {
this.setState({
inClockComponent: false,
inHelloWorldComponent: true,
inOtherComponent: false
)}
}
You insert that function onto a button that would navigate to a different component
Example
// in Clock Component
render() {
return (
<div>
<h2>Time is 5:15 P.M.</h2>
<button onClick={this.props.showHelloWorldComponent}>
Go To Hello World
</button>
)
}
It's a messy solution, and I wouldn't suggest using it in a big application, but I hope this answers your question!

Categories

Resources