How do I pass props from from redux store - javascript

I have a simple app that's using redux and react-router. I wrapped my app component in a provider tag so that it has access to the store. I connected (in App.js) the mapStateToProps and mapStateToDispatch in the App.js. I'm not sure how to pass the function I defined in App.js to a child component since I'm using route. I tried doing the render trick but it didn't work. If I can pass it to that CelebrityPage component, how would I receive it in the file? Any help would be appreciated.
This is my App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import './App.css';
import Clarifai from 'clarifai'
// import Particles from 'react-particles-js';
// import particlesOptions from './particleOptions'
import { Signin } from './components/signin/Signin';
import Register from './components/register/Register';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { setSearchField } from './context/Actions'
import FacePage from './Containers/FacePage';
import CelebrityPage from './Containers/CelebrityPage';
import ControllerPage from './Containers/ControllerPage';
const mapStateToProps = state => {
return {
input: state.input
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleSearchChange: (event) => dispatch(setSearchField(event.target.value))
}
}
...
render() {
return (<Router>
<Switch >
<Route path='/celebrity' exact render={props => <CelebrityPage{...props} handleSearchChange={this.handleSearchChange} />} />
<Route path='/' exact component={Register} />
<Route path='/signin' exact component={Signin} />
<Route path='/contoller' exact component={ControllerPage} />
<Route path='/face-detection' exact component={FacePage} />
</Switch>
</Router>)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)

If you are going to pass store actions and states into the child components, it means you are refusing to use the advantages of redux. The best approach should be connect any of your component that needs to access to the actions or state to the store. Doing connection at the root component level and passing the props to the child components is not a good solution.

I think what robert is saying is what you'd probably want to do. Don't try to pass your props inside of your <Route>. Instead do your connect mapDispatchToProps and your mapStateToProps inside your CelebrityPage Component.
Once you do the wrapping inside of the Celebrity Page component you should have access to the props and functions that you have defined.
...
// keep all the previous imports from your App.Js
render() {
// have your router like this
return (<Router>
<Switch >
<Route path='/celebrity' exact component ={CelebrityPage} />
<Route path='/' exact component={Register} />
<Route path='/signin' exact component={Signin} />
<Route path='/contoller' exact component={ControllerPage} />
<Route path='/face-detection' exact component={FacePage} />
</Switch>
</Router>)
}
}
export default App
Example Celebrity page
import React from 'react'
import { connect } from 'react-redux'
class CelebrityPage extends React.Component {
// put your mapStateToProps and mapDispatch function heres instead of app.js
mapStateToProps() {
}
mapDispatchToProps {
// bind your handlesearch function to props here
}
render() {
return (
<div>
<input />
<button onClick={this.props.handleSearchChange}/>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CelebrityPage)

Related

React.js routing keeps rendering my main App regardless of URL

I am running an issue where, regardless of what URL I am putting into my browser, I keep getting routed to my main page. I've posted the code below for you to take a look, but my goal is to have my browser take me to my drivers.jsx component when the URL is localhost:3000/drivers. Currently, when I go to localhost:3000/drivers, it renders my _app.jsx component instead :(. Can someone help me understand why I can never render the Drivers component (in drivers.jsx) when I am at localhost:3000/drivers?
index.jsx:
import { BrowserRouter as Router, Routes, Switch, Route } from 'react-router-dom';
import MyApp from './_app.jsx';
import Drivers from './drivers.jsx'
import React, { Component } from 'react'
import { Link } from "../routes.js"
class Home extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path='/drivers' element = {<Drivers />}> </Route>
<Route exact path='/' element = {<MyApp />}> </Route>
</Switch>
</Router>
);
}
}
export default Home;
_app.jsx
import React, { Component } from 'react';
import { useLoadScript } from '#react-google-maps/api';
import Map from '../components/map.jsx';
import "../styles/globals.css";
const MyApp = () => {
const libraries = ['places'];
const {isLoaded} = useLoadScript({
googleMapsApiKey: XXXXXXXXXXXXXX,
libraries
});
if (!isLoaded) return <div>Loading...</div>;
return (
<Map />
);
}
export default MyApp;
drivers.jsx
import React, { Component } from 'react';
class Drivers extends Component {
render() {
return (
<div>TEST</div>
);
}
}
export default Drivers;
I've tried putting the routing logic inside _app.jsx instead, but that causes an incredible amount of errors. My thought is index.js should host all the routing logic, but it shouldn't keep rendering MyApp instead of Drivers when the route is "localhost:3000/drivers".
if your react-router-dom version is
6.4.3
then the switch component dosen't work try changing code to this
instead of using Switch. wrap Route inside Routes
<Router>
<Routes>
<Route path='/drivers' element = {<Drivers />}> />
<Route exact path='/' element = {<MyApp />}> />
</Routes>
</Router>
like this

No `render` method found on the returned component instance: you may have forgotten to define `render`

import React, { Component } from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
interface PrivateRoute {
isLoggedIn: boolean;
path: string;
component: any;
exact: boolean;
}
const PrivateRoute: React.FC<PrivateRoute> = ({
isLoggedIn,
component,
...rest
}) => {
return (
<Route
{...rest}
component={(props: any) =>
isLoggedIn ? <Component {...props} /> : <Redirect to="/" />
}
/>
);
};
const mapStateToProps = (state: any) => ({
isLoggedIn: !!state.auth.uid,
});
export default connect(mapStateToProps)(PrivateRoute);
So this is the component I have, for creating private routes. And the router is this;
import HeaderTop from "./HeaderTop/HeaderTop";
import HeaderBottom from "./HeaderBottom/HeaderBottom";
import Body from "./Body/Body";
import Footer from "./Footer/Footer";
import Movies from "./ShowPages/Movies";
import Series from "./ShowPages/Series";
import NotFoundPage from "./NotFoundPage/NotFoundPage";
import { Router, Switch, Route } from "react-router-dom";
import LoginPage from "./LoginPage/LoginPage";
import { createBrowserHistory } from "history";
import PrivateRoute from "./PrivateRoute";
export const history = createBrowserHistory();
const HomePage: React.FC = () => {
return (
<Router history={history}>
<div>
<HeaderTop />
<HeaderBottom />
<Switch>
<Route path="/" component={LoginPage} exact={true} />
<PrivateRoute path="/meerkast" component={Body} exact={true} />
<PrivateRoute path="/movies" component={Movies} exact={true} />
<PrivateRoute path="/series" component={Series} exact={true} />
<Route component={NotFoundPage} />
</Switch>
<Footer />
</div>
</Router>
);
};
export default HomePage;
So what is going on here? I don't have class component why would I have render method anywhere? I found this question is asked many times on stackoverflow but no one had the proper answer.
Your issue is with inconsistant casing on the component prop. Your PrivateRoute gets a prop component with a lowercase c. When you call <Component {...props} /> with an uppercase C, that Component variable doesn't exist.
I understand the reason that you used uppercase in the JSX, which is that React interprets lowercase names as built-in elements. I also understand the reason that you used lowercase in the PrivateRoute props, which is that you wanted to be be consistent with react-router-dom's Route props.
But we have to make the two coexist. We do this by renaming the lowercase component to Component in the spread expression.
const PrivateRoute: React.FC<PrivateRoute> = ({
isLoggedIn,
component: Component,
...rest
}) => {

TypeError: Cannot read property 'params' of undefined at App React Router

Whenever I console log props.match.params, I get an error:
TypeError: Cannot read property 'params' of undefined at App. I'm not sure this is relevant, but even if I console.log(props) I get four empty arrays.
Here is all the relevant code:
Home.js
import React from "react";
import App from "./App";
import { render } from "react-dom";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
} from "react-router-dom";
const Home = () => {
return (
<div>
<Router>
<Route exact path="/">
<App />
</Route>
<Route path="/:roomCode" component={App} />
</Router>
</div>
);
};
export default Home;
App.js (only the relevant part)
const App = (props) => {
console.log(props.match.params);
};
export default App;
const appDiv = document.getElementById("app");
render(<App />, appDiv);
I have been trying to figure this out for the past two days. Nothing works. Also, history.push also doesn't work, returns a very similar error. I have a feeling react-router-dom is broken in my project.
Help is much appreciated.
Edit:
Here is the codesandbox: https://codesandbox.io/s/reverent-microservice-iosu2?file=/src/App.js
Your Home Component is the root of all your components so it needs to be pass to render function not your App which is a descendent of Home.
after that change you need to change this line in your Home Component:
<Route exact path="/" render={(props) => <App {...props} />} />
import React from "react";
import App from "./App";
import { render } from "react-dom";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
} from "react-router-dom";
const Home = () => {
return (
<div>
<Router>
<Route exact path="/" render={(props) => <App {...props} />} />
<Route path="/:roomCode" component={App} />
</Router>
</div>
);
};
export default Home;
here is how to render it:
const appDiv = document.getElementById("app");
render(<Home />, appDiv);
now you can get the props
const App = (props) => {
console.log(props.match.params);
};
export default App;
You could try using the React hooks provided by the React Router framework. There are several different hooks used to interact with the router.
const App = () => {
const { roomCode } = useParams()
console.log(params);
};
More info here

React router - accessing props within route

I'm new to React Router and i'm trying to pass down some props from one of my parent components down to a child one through a route.
Here is my code:
import React, {Component} from 'react';
import Sitefeedback from '../sections/comments/site-feedback';
import { BrowserRouter,Switch, Route } from 'react-router-dom';
class Outer extends Component {
constructor(props) {
super(props);
}
myFunction = () => {
console.log("hello");
}
render() {
<div className="app-content-wrap-inner">
<BrowserRouter>
<switch>
<Route path='/site-feedback' render={(props) => (<Sitefeedback {...props} />)}/>
<Route path='/logins' component={Logins} />
</switch>
</BrowserRouter>
</div>
}
}
Ideally I would like to be able to trigger myFunction from the child component. After doing some research I thought passing the Route a render option instead of component would pass it down however it doesn't. Any idea how I can get the function passed down into the routes component?
I don't know if you still need help with this, but if you do you can do it like this
<Route path='/site-feedback' render={props => (<Sitefeedback {...props} />)}/>

Programmatically Navigate using react-router

I am developing an application in which I check if the user is not loggedIn. I have to display the login form, else dispatch an action that would change the route and load other component. Here is my code:
render() {
if (isLoggedIn) {
// dispatch an action to change the route
}
// return login component
<Login />
}
How can I achieve this as I cannot change states inside the render function.
Considering you are using react-router v4
Use your component with withRouter and use history.push from props to change the route. You need to make use of withRouter only when your component is not receiving the Router props, this may happen in cases when your component is a nested child of a component rendered by the Router and you haven't passed the Router props to it or when the component is not linked to the Router at all and is rendered as a separate component from the Routes.
import {withRouter} from 'react-router';
class App extends React.Component {
...
componenDidMount() {
// get isLoggedIn from localStorage or API call
if (isLoggedIn) {
// dispatch an action to change the route
this.props.history.push('/home');
}
}
render() {
// return login component
return <Login />
}
}
export default withRouter(App);
Important Note
If you are using withRouter to prevent updates from being blocked by
shouldComponentUpdate, it is important that withRouter wraps the
component that implements shouldComponentUpdate. For example, when
using Redux:
// This gets around shouldComponentUpdate
withRouter(connect(...)(MyComponent))
// This does not
connect(...)(withRouter(MyComponent))
or you could use Redirect
import {withRouter} from 'react-router';
class App extends React.Component {
...
render() {
if(isLoggedIn) {
return <Redirect to="/home"/>
}
// return login component
return <Login />
}
}
With react-router v2 or react-router v3, you can make use of context to dynamically change the route like
class App extends React.Component {
...
render() {
if (isLoggedIn) {
// dispatch an action to change the route
this.context.router.push('/home');
}
// return login component
return <Login />
}
}
App.contextTypes = {
router: React.PropTypes.object.isRequired
}
export default App;
or use
import { browserHistory } from 'react-router';
browserHistory.push('/some/path');
In react-router version 4:
import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
const Example = () => (
if (isLoggedIn) {
<OtherComponent />
} else {
<Router>
<Redirect push to="/login" />
<Route path="/login" component={Login}/>
</Router>
}
)
const Login = () => (
<h1>Form Components</h1>
...
)
export default Example;
Another alternative is to handle this using Thunk-style asynchronous actions (which are safe/allowed to have side-effects).
If you use Thunk, you can inject the same history object into both your <Router> component and Thunk actions using thunk.withExtraArgument, like this:
import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
import { createBrowserHistory } from "history"
import { applyMiddleware, createStore } from "redux"
import thunk from "redux-thunk"
const history = createBrowserHistory()
const middlewares = applyMiddleware(thunk.withExtraArgument({history}))
const store = createStore(appReducer, middlewares)
render(
<Provider store={store}
<Router history={history}>
<Route path="*" component={CatchAll} />
</Router
</Provider>,
appDiv)
Then in your action-creators, you will have a history instance that is safe to use with ReactRouter, so you can just trigger a regular Redux event if you're not logged in:
// meanwhile... in action-creators.js
export const notLoggedIn = () => {
return (dispatch, getState, {history}) => {
history.push(`/login`)
}
}
Another advantage of this is that the url is easier to handle, now, so we can put redirect info on the query string, etc.
You can try still doing this check in your Render methods, but if it causes problems, you might consider doing it in componentDidMount, or elsewhere in the lifecycle (although also I understand the desire to stick with Stateless Functional Compeonents!)
You can still use Redux and mapDispatchToProps to inject the action creator into your comptonent, so your component is still only loosely connected to Redux.
This is my handle loggedIn. react-router v4
PrivateRoute is allow enter path if user is loggedIn and save the token to localStorge
function PrivateRoute({ component: Component, ...rest }) {
return (
<Route
{...rest}
render={props => (localStorage.token) ? <Component {...props} /> : (
<Redirect
to={{
pathname: '/signin',
state: { from: props.location },
}}
/>
)
}
/>
);
}
Define all paths in your app in here
export default (
<main>
<Switch>
<Route exact path="/signin" component={SignIn} />
<Route exact path="/signup" component={SignUp} />
<PrivateRoute path="/" component={Home} />
</Switch>
</main>
);
Those who are facing issues in implementing this on react-router v4. Here is a working solution for navigating through the react app programmatically.
history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
App.js OR Route.jsx. Pass history as a prop to your Router.
import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
<Route path="/test" component={Test}/>
</Router>
You can use push() to navigate.
import history from './history'
...
render() {
if (isLoggedIn) {
history.push('/test') // this should change the url and re-render Test component
}
// return login component
<Login />
}
All thanks to this comment: https://github.com/ReactTraining/react-router/issues/3498#issuecomment-301057248
render(){
return (
<div>
{ this.props.redirect ? <Redirect to="/" /> :'' }
<div>
add here component codes
</div>
</div>
);
}
I would suggest you to use connected-react-router https://github.com/supasate/connected-react-router
which helps to perform navigation even from reducers/actions if you want.
it is well documented and easy to configure
I was able to use history within stateless functional component, using withRouter following way (needed to ignore typescript warning):
import { withRouter } from 'react-router-dom';
...
type Props = { myProp: boolean };
// #ts-ignore
export const MyComponent: FC<Props> = withRouter(({ myProp, history }) => {
...
})
import { useNavigate } from "react-router-dom"; //with v6
export default function Component() {
const navigate = useNavigate();
navigate.push('/path');
}
I had this issue and just solved it with the new useNavigate hook in version 6 of react-router-dom

Categories

Resources