React how re-mount component after route change - reactjs

i'm using react-redux in my project.
I have some component where i'm dispatch some action and get data from reducer into a component on componentWillMount method here is problem when i change to route componentWillMount method not firing my actions or anything else. for that i need to refresh page.
here is my componentWillMount Method:
componentWillMount() {
console.log("d")
this
.props
.getChartData()
data = this.props.chartData
}
and here is my main.js where i import my components and assign routing.
import CompA from 'components/CompA'
import CompB from 'components/CompB'
const App = () => (
<Container fluid>
<div>
<main>
<Switch>
<Route exact path="/page-a" component={ComponentA} />
<Route exact path="/page-b" component={ComponentB} />
</Switch>
</main>
</div>
</Container>
)
export default App
where i mistake or what i need to do with it?
and its index.js:
const target = document.querySelector('#root')
console.log(store.getState())
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div className="row">
<App />
</div>
</ConnectedRouter>
</Provider>,
target
)

if you use redux you should use mapStateToProps to change the state in your component

I think you are using ConnectedRouter from the package react-router-redux, which is now deprecated.
For react-router 4, You need to use withRouter react-router-dom to wrap the component before connecting it like this:
withRouter(connect(...)(MyComponent))
You can get also get the match, location, history properties via props once the component is wrapped in withRouter.
For further information on withRouter, you can visit the following link: https://reacttraining.com/react-router/web/api/withRouter

Related

How to access history globally in react

In react I can use this.props.history in Route components or using withRouter. But I want to access this globally from others components.
Is there any way? Thanks in advance.
Assuming you're using at least React 16.8.0 when hooks were introduced, the useHistory hook will give you access to the history instance. This is essentially the modern equivalent of the withRouter HOC you mention in your question. Is there a reason this does not fit your needs?
You mention accessing it "globally", which makes me think you're looking for something like assigning the history instance to window, which you could of course do, but the hook/hoc are the more canonical approaches to getting access to history from a component.
As the comment said, you can use react context, or redux to pass your history to all the child components.
And, here is one solution about how you can catch the history via local HOC.
component={BK} => component={RouteWithHistory(BK)}
ReactDOM.render(
<Provider store={store}>
<ThemeProvider theme={theme}>
<BrowserRouter>
<Switch>
<Route exact path="/books" component={BK} /> // Before
<Route exact path="/books" component={RouteWithHistory(BK)} /> // After
<Route path="/details/" component={DT} />
<Redirect to="/" />
</Switch>
</BrowserRouter>
</ThemeProvider>
</Provider>
,document.getElementById('root')
);
And the main function
interface Props {
history: {location: {pathname: string}};
}
const RouteWithHistory = (Component: React.FC) => (props: Props) => {
const pathname = props.history.location.pathname;
console.log(pathname);
// You can use redux here.
// Or use the react context as below.
const ThemeContext = React.createContext('');
return (
<ThemeContext.Provider value={pathname}>
<Component />
</ThemeContext.Provider>
)
}

when I use history.push url changes but doesn't render the component

I've seen problems like this but neither of them solved my problem.
I have a p tag that onClick is supposed to render a page.
But when I click on it and use this.props.history.push(/posts/${id}) It changes the url but doesn't render the component I want.
I have tried using a
onClick={(e, id) => this.postPage(e, post.id)}
to trigger a function that will trigger the component I want to show up. I also tried using
<Link to-=''>
insted of
<p onClick>
but it doesn't work either.
This is what I have done so far, this is the function that triggers when I click the p tag:
postPage = (e, id) => {
const { history } = this.props;
history.push(`/post/${id.toString()}`);
}
and also, I used withRouter to have access to the history prop.
export default withRouter(connect(null, mapDispatchToProps)(Post));
And these are the routes I mapped:
render() {
return (
<div className="Routes">
<Switch>
<Route exact path='/' component={Home}></Route>
<Route path='/post/:id' render={(history) => (
<PostPage {...history}></PostPage>
)}></Route>
</Switch>
</div>
);
}
}
And of course, when I call this component I also pass using BrowserRouter
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Routes />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
I just wanted to render the component but only the URL changes and nothing happens.
A good approach would be to implement react-redux and react-router-dom one at a time, to ensure that there are no clashes. The double HOC here:
export default withRouter(connect(null, mapDispatchToProps)(Post));
Is worth breaking down to:
export default withRouter(Post);
To ensure that react-router-dom works as expected, then implementing react-redux once routing is in place.
For those who have had the same problem the solution was to change the BrowserRouter tag location from the index.js to inside of the component that uses the Route. I was doing something like:
ReactDOM.render(<Provider>
<BrowserRouter>
<App>
</BrowserRouter>
</Provider>, document.getElementById('root'));
and now inside the Routes.js component
render() {
<BrowserRouter>
<Route ...>
<Route ...>
</BrowserRouter>
}

ReactJs Router Duplicates itself when wrap it in component

I'm beginner in react, I'm using React 4, Redux and react-router-dom package for my router.
I have an App.js file which connects Main with mapStateToProps & mapDispatchToProps
//App.js
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
My Main component renders a header and clones child elements
//Main.js
export default class Main extends React.Component {
render() {
return (
<div>
<h1>Hi I'm Header</h1>
{React.cloneElement(this.props.children, this.props)}
</div>
);
}
}
My child components contains a simple Json stringify of props
//UserList.js
export default class UsersList extends React.Component {
render(){
return (
<pre>
{JSON.stringify(this.props, null, "")}
</pre>
);
}
}
I made an index.js file which has a route and contains provider, and provider child is a router and my router contains other app routes
//index.js
const router = (
<Provider store={store}>
<App>
<Router history={history}>
<Switch>
<Route exact path="/" component={UsersList}/>
</Switch>
</Router>
</App>
</Provider>
);
render(router, document.getElementById('root'));
As you see I have an App, my app connects main to mapstatetoprops and mapdispatchtoprops, and finally gives the child route (UsersList component) to the parent (Main Component) and in the end it renders the child.
When I stringify the child component props it show only history, location and other props of route, but it's not showing my Users props which is the default state of the app!
And when I use react developer tools it shows there is other router inside my router!
Duplicated Router Screenshot
I found out that when I put the router in something (component, element or whatever like ) it creates another router inside itself and the child router doesn't have the parent props.
I also tried to add
{...this.props}
to routes or
render={(props) => <UserList {props} />}
and also another way that I tried was using nested routes like
const router = (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route exact component={UsersList}/>
<Route path="/single/:id" component={SingleUser}/>
</Route>
</Router>
</Provider>
);
but the result was this error:
React.cloneElement(...): The argument must be a React element, but you passed undefined.
Thanks.
Since you want to pass the props from App component to UsersList, you need to restructure your Routes differently since with the current way, the props are passed on to the Router, but Router doesn't pass them down to the Route component
index.js
const router = (
<Provider store={store}>
<Router history={history}>
<Route component={App} />
</Router>
</Provider>
);
render(router, document.getElementById('root'));
Main.js
export default class Main extends React.Component {
render() {
return (
<div>
<h1>Hi I'm Header</h1>
<Switch>
<Route exact path="/" render={(routerProps) => <UsersList {...this.props} {...routerProps}/>}/>
</Switch>
</div>
);
}
}

React Redux Router Component Has Empty State

I am writing an react website that has a master-detail feel to it. I make server calls to populate my master component. When a user clicks an item on the master component, it uses React Router to navigate to a detail view. However, my state is no longer populated. Instead it appears I'm getting my initial state in this new component.
As far as I know, I'm using the latest on all the libraries.
index.js
const store = storeFactory( initialState );
const myHistory = createBrowserHistory();
ReactDOM.render(<Provider store={store}>
<Router history={ myHistory }>
<div>
<Route exact path='/' component={App} />
<Route exact path='/contrib' component={Contributions} />
<Route exact path='/story/:id' component={FullStory} />
</div>
</Router>
</Provider>,
document.getElementById('root'));
Story.jsx (a child of App)
<div className="faux-row">
<Link className="invisible-link" to={`/story/${this.props.story.id}`}>
<div className="story">
<div className="story-header">
FullStory.jsx
const mapStateToProps = (state) => {
console.log(state);
return {
stories: state.story_groups
}
}
story_groups as shown here is empty. How do I keep the state?
EDIT:
I am using redux-sage for middleware to call the server. The dispatch is coming out of ComponentWillMount() in the App component.

Programmatically Navigate w/ HashRouter

I was originally using hashHistory from react-router and programmatically navigating my React app using this.props.history.push(). But with the move to using HashRouter from the react-router-dom package, this.props.history.push() now throws a Cannot read property 'push' of undefined error.
How do I programmatically navigate using HashRouter now?
ReactDOM.render(
<Provider store={store}>
<MuiThemeProvider>
<HashRouter>
<div>
<Route exact path="/" component={App} />
<Route path="/landing" component={Landing} />
</div>
</HashRouter>
</MuiThemeProvider>
</Provider>,
document.getElementById('root'));
Render func of App.js
render() {
return (
<div className="App">
...
<div>
<Collapse isOpened={this.state.activeForm === 1}>
<SignUpForm />
</Collapse>
<Collapse isOpened={this.state.activeForm === 2}>
<SignInForm />
</Collapse>
...
</div>
);
}
Function in SignUpForm.js that's calling .push()
handleSubmit(e) {
...
this.props.history.push('/landing');
...
}
If your component is rendered from a Route (like App and Landing), then you will have access to all router related props (match, history, location). However, since SignUpForm isn't rendered with a Route, you won't have access to those props by default.
But we can use withRoute higher-order component to explicitly get those props. We just need to wrap the component with this HoC before exporting it as follows.
export default withRouter(SignUpForm);
Now it will work as expected since SignUpForm gets history prop.
I have figured out what my issue is. My index.js is set up as such (below) with App.js as one of the routes, not SignUpForm.js, which is a container being rendered in App.js. This means that SignUpForm.js has no notion of the history prop within App.js.
By passing App.js's history prop to SignUpForm.js as a prop, I was able to call this.props.history.push() within SignUpForm.js to get my desired action.

Resources