Can not read history in class component - javascript

I am trying to navigate to other page by using button click but it is throwing error by sayng that can not read property push of undefined.
I have did some research on history which is used forfunctional component.
How i can make use of history in class component or what is other way (react way) to navigate other page.
here is my code.
import React, { Component } from 'react';
class CentralBar extends Component {
constructor(props){
super(props)
this.state = {
}
this.someText = this.someText.bind(this);
}
someText(){
this.props.history.push(`/login/`);
}
render() {
return (
<div className="crentralPanel">
<button type="button" class="btn btn-light " onClick={this.someText}>Click on Text</button>
</div>
);
}
}
export default CentralBar;

history can be used in a class component. You just get access to it in a different way than in a function component.
In a function component, you would typically use the useHistory hook. But since hooks are exclusive to function components, you have to use a different method in a class.
Probably the simplest way is to use the withRouter higher order component. The only change you need to make is adding the import at the top, and wrapping the export.
import { withRouter } from 'react-router-dom';
export default withRouter(CentralBar);
This wrapper injects 3 props into the component: history, match, and location.
Note that the same rules apply to the HOC as do the useHistory hook since it's just another way of reading from the react-router context: the component must be a child of a Router provider component.

Please wrap your class component with withRouter.
import { withRouter} from 'react-router-dom';
export default withRouter(CentralBar);
If you didn't install react-router-dom module, please run this command to install this node module.
Please let me know if it works or not.

You can't use hooks in class components. You can find many solution in the Internet how to get it around, but I don't think it is worth it. If you really need use hooks in this component, make it with Functional Component. Class components should be used in react in small amount of cases. You can read about it here: React functional components vs classical components

Related

How to route to or navigate to a route programmatically in nextjs class component?

I am building a simple app using nextjs. most of my components are functional components. However, I have a class component to handle form.
I want to redirect to the home page after the form is submitted. console.log(this.router) gives undefined. Since it is a class component I can't use useRouter() hook. How do I get reference to router in class components in next.js?
Finally I got the answer,
import Router from 'next/router'
and use Router.push(...)
With Next 13 onwards, import router from next/navigation
As an alternative to using the next/router default export directly, withRouter HOC can also add the router object to any component.
import * as React from 'react'
import { withRouter } from 'next/router'
class Page extends React.Component {
render() {
const { router } = this.props
return <p>{router.pathname}</p>
}
}
export default withRouter(Page)

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

Why HOC are applied during exporting of component in place of importing it

My basic understading is that HOC like connect (for connecting with redux store) and other HOC's are applied to a component while exporting it.
Like this
import React, { Component } from 'react';
import './App.css';
import myHoc from './myHoc/index';
class App extends Component {
render() {
return (
<div className="App">
</div>);
}
}
export default myHoc({})(App);
Where as a better thing would be to apply HOC during import as it would make it easier to make reusable component. The same component can pick up props from store or from props and that would be the responsibility of the parent component to check what to give which HOC to apply on the component.
I know we can use container components which takes the component and render children but that just adds code in the JSX (wont look good if there are many container components)
though we can do it like this
import React, { Component } from 'react';
import './App.css';
import myHoc from './myHoc/index';
import AppChild from './AppChild';
const NewAppChild = myHoc({}, ()=> {
})(AppChild);
class App extends Component {
state = {
count: 1,
};
reRender = () => {
this.setState({count: this.state.count + 1});
};
render() {
return (
<div className="App">
<NewAppChild handleClick={this.reRender} count={this.state.count}/>
</div>
);
}
}
export default App;
What my question is that, is there something better that can handle this kind of situations where I want to apply my HOC on import that is each many container components can import it and they can apply different HOCs depending on the needs.
There is no single concrete reason for this design choice - as you have already seen you can invoke your HOC wherever you use the component - but I see at least 1 advantage: configuration & component reuse.
In your example, myHoc takes no parameters or configuration so this doesn't necessarily apply, but imagine instead that you are invoking connect from redux.
In most use cases, connect accepts 2 configuration functions -
mapStateToProps & mapDispatchToProps - that define the behaviour. If you define those within MyComponent then any consuming component can import MyComponent from 'MyComponent' and start using it.
If you instead rely on the parent component to call connect() then you are forcing every consumer to re-implement the configuration of connect as well. That may mean many instances of duplicated configuration and adds to the complexity for consuming components.
That being said, there are certainly cases where you might want this behaviour - for example, if you wanted to connect the same component to different state definitions. Ultimately you need to pick the best pattern to support what you need from the component.

Construct React Component from a React Element

I'm trying to create a "higher-order" function in React that performs some permissions-based checks on the wrapped component and returns it accordingly.
MyComponent.js
...
export default Permissions(MyComponent)
Permissions.js
export default function Permissions(Component) {
class NewComponent extends React.Component {
// ... perform checks here
render() {
return {validPermissions && <Component />}
}
}
}
However, I'd like to be able to use this Permissions as a React Component (as opposed to a function that wraps the component export).
It would looks similar to this:
<Permissions>
<MyComponent />
</Permissions>
When I run React.Component.isPrototypeOf(Component.children) I get false in these instances. My inclination is to think that the solution is to use some React or ReactDOM method to transform the React Element into a React Component, and then perform the same checks.
How can I transform a React Element into a React Component?
Update:
I gave the bit about permissions as context, but not looking for help with regard to implementing permissions.
I am basically looking for the opposite of React.createElement(MyComponent).
You can use a functional component, which combines the best of both worlds: it's simple (just a function!) and at the same time it's a proper stateless React component.
const Permissions = ({ granted, children }) =>
granted ? React.Children.only(children) : null;
Usage:
<Permissions granted={true}>
<MyComponent />
</Permissions>

Categories

Resources