I want to push some text to url route in onChange method of an input like this :
function Header(props) {
return (
<div>
<input type="radio" onChange={ (e)=> props.history.push(`/${e.target.value}`) } >
</div>
)
But it throws multiple errors and crashes :
TypeError: Cannot read property 'push' of undefined
How can use history.push properly or how can I push some text to route url manually from anywhere in react ?
Error message suggests that history prop is undefined, meaning its not passed to your component as a prop which can happen depending on the hierarchy of the components in your app.
Use one of the following options to get access to history:
Use useHistory hook
import { useHistory } from 'react-router-dom';
function Header(props) {
const routerHistory = useHistory();
return (
<div>
<input type="radio" onChange={(e)=> routerHistory.push(`/${e.target.value}`)}>
</div>
)
}
Use withRouter higher order component
import { withRouter } from 'react-router-dom';
function Header(props) {
return (
<div>
<input type="radio" onChange={(e)=> props.history.push(`/${e.target.value}`)}>
</div>
)
}
export default withRouter(Header);
For details on withRouter and useHistory, see:
React Router - useHistory Hook
React Router - withRouter
Related
We use an external componet which we don't control that takes in children which can be other components or
used for routing to another page. That component is called Modulation.
This is how we are currently calling that external Modulation component within our MyComponent.
import React, {Fragment} from 'react';
import { withRouter } from "react-router";
import { Modulation, Type } from "external-package";
const MyComponent = ({
router,
Modulation,
Type,
}) => {
// Need to call it this way, it's how we do modulation logics.
// So if there is match on typeA, nothing is done here.
// if there is match on typeB perform the re routing via router push
// match happens externally when we use this Modulation component.
const getModulation = () => {
return (
<Modulation>
<Type type="typeA"/> {/* do nothing */}
<Type type="typeB"> {/* redirect */}
{router.push('some.url.com')}
</Type>
</Modulation>
);
}
React.useEffect(() => {
getModulation();
}, [])
return <Fragment />;
};
export default withRouter(MyComponent);
This MyComponent is then called within MainComponent.
import React, { Fragment } from 'react';
import MyComponent from '../MyComponent';
import OtherComponent1 from '../OtherComponent1';
import OtherComponent2 from '../OtherComponent2';
const MainComponent = ({
// some props
}) => {
return (
<div>
<MyComponent /> {/* this is the above component */}
{/* We should only show/reach these components if router.push() didn't happen above */}
<OtherComponent1 />
<OtherComponent2 />
</div>
);
};
export default MainComponent;
So when we match typeB, we do perform the rerouting correctly.
But is not clean. OtherComponent1 and OtherComponent2 temporarily shows up (about 2 seconds) before it reroutes to new page.
Why? Is there a way to block it, ensure that if we are performing router.push('') we do not show these other components
and just redirect cleanly?
P.S: react-router version is 3.0.0
I'm trying to build a weather application using openweathermap api. As you can see below in the code, I use a boolean state to check when the form is summitted so I can pass that value to the <Result> component(which I checked with hard code and it works). I want the function changeCity(in the App) to return the <Result> component with the value of city passed and in the same time to change the cityEmpty state. But there I got the problem when I pass that in the return() {(cityEmpty) ? changeCity() : null}
import React, {useState} from 'react';
import Result from "./components/Result";
import Search from "./components/Search";
import './App.css';
function App() {
const [city, setCity] = useState ("");
const [cityEmpty, setCityEmpty] = useState(false);
const changeCity = () => {
setCityEmpty(false);
return (<Result city={city}/>);
}
return (
<div className="App">
<Search city={city} setCity={setCity} cityEmpty={cityEmpty} setCityEmpty={setCityEmpty}
/>
{(cityEmpty) ? changeCity() : null}
</div>
);
}
export default App;
import React from "react"
function Search({city, setCity, cityEmpty, setCityEmpty}){
const handleInputChange = (e) => {
setCity(e.target.value);
}
const handleSumbit = (e) => {
e.preventDefault();
console.log(cityEmpty);
setCityEmpty(true);
console.log(cityEmpty);
setCity("");
}
return(
<div>
<form onSubmit={handleSumbit}>
<input
type="text"
placeholder="Insert city"
value={city}
onChange = {handleInputChange}
>
</input>
</form>
</div>
);
}
export default Search
You can't call a state update inside the render of a component. Remember that whenever state is updated, it will re render the component. This will cause an infinite loop in your component.
Component renders
changeCity is called
Inside changeCity, setCityEmpty is called
Go back to step 1 for rendering.
Instead, consider checking if city is empty in your handleInputChange and calling setCityEmpty inside that function.
EDIT: To clarify a function that returns a component is completely fine, this is all components are really. Functions (or in previous react versions: classes) returning other components.
you don't have to return JSX from function. In your case it is pretty straightforward to use.
import React, {useState} from 'react';
import Result from "./components/Result";
import Search from "./components/Search";
import './App.css';
function App() {
const [city, setCity] = useState ("");
const [cityEmpty, setCityEmpty] = useState(false);
return (
<div className="App">
<Search city={city} setCity={setCity} cityEmpty={cityEmpty} setCityEmpty={setCityEmpty}
/>
{cityEmpty && <Result city={city}/>}
</div>
);
}
export default App;
import React from "react"
function Search({city, setCity, cityEmpty, setCityEmpty}){
const handleInputChange = (e) => {
setCity(e.target.value);
}
const handleSumbit = (e) => {
e.preventDefault();
console.log(cityEmpty);
setCityEmpty(true);
console.log(cityEmpty);
setCity("");
}
return(
<div>
<form onSubmit={handleSumbit}>
<input
type="text"
placeholder="Insert city"
value={city}
onChange = {handleInputChange}
>
</input>
</form>
</div>
);
}
export default Search
Not sure what the problem you're seeing is but I noticed you're setting state inside of a render function, which is a bad pattern. Any state changes will trigger a re-rendering of the component and if you set state within a render function then you'd have an infinite loop of re-renderings (but.
Try removing setCityEmpty(false) in changeCity.
const changeCity = () => {
return (<Result city={city}/>);
}
So how would you update cityEmpty? It's not clear what the end goal is here. With more info, we can find a better implementation.
I am using a toggleswitch component in my react app. Within every event change of the toggle button, I want to change my routing, inside this component. Using react routing for the first time so I am pretty confused how can I handle it within the component.
If the state is true, I want to route it to "/", else i want it to "/videos". Here is my code :
import React, { Component } from "react";
class ToggleSwitch extends Component {
constructor() {
super();
this.state = {
toggleValue: false
};
this.changeToggleMenuValue = this.changeToggleMenuValue.bind(this);
}
changeToggleMenuValue(event) {
this.setState({
toggleValue: !this.state.toggleValue
});
}
render() {
return (
<div className="onoffswitch">
<input
type="checkbox"
name="onoffswitch"
class="onoffswitch-checkbox"
id="myonoffswitch"
onClick={e => this.changeToggleMenuValue(e)}
/>
<label class="onoffswitch-label" for="myonoffswitch">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
);
}
}
export default ToggleSwitch;
the base routing structure in react is like below:
1.Root Component:
basically you have a Root Component in your application, mainly the <App /> component
2.Inner Components:
inside of your <App /> component you render 2 type of components:
components that should render in your routes
components that are shared between your routes ( which means that they are visible in every route )
type 2 components would render on each route, because they are out of Switch, like code structure below:
function App(props) {
return (
<>
<Header />
<Switch>
...your routes
</Switch>
<SharedComponent_1 /> // may be a notif manager
<SharedComponent_2 /> // may be a footer
<SharedComponent_1 /> // other type of shared component
</>
)
}
if you want to navigate from a route to another route inside of your component logic, you should ask your self two questions about your component:
is my component directly rendered by a <Route ... />?
is my component is just a simple sub component that rendered by another component which rendered by a <Router ... />
if your component has criteria of condition 1, then you already have history, match, location in your props and you can use history.push('/targetRoute') to navigate your user to another route.
however, if your component has criteras described in condition 2, you should wrap your component by a withRouter to get history, match and location into your props and user push function of history to navigate user around.
This function should be work on your code:
changeToggleMenuValue() {
this.setState({
toggleValue:
this.state.toggleValue
? history.push('') // or history.push('/')
: history.push('/videos');
});
}
I want to pass some value from one component to another once the user clicks a button.
First Component (Survey) with a value survey --> User clicks a button --> Second component (Details) renders the survey
To achieve so I am using Redirect from the react-router-dom.
function Survey({survey}) {
....
// There is a button that triggers the below if clause. The redirect works but it seems the state is not sent.
if (surveyPage === true) {
return <Redirect to={{
pathname: '/detail',
state: { location: survey }
}}
}
return("something")
}
Second component
function Details(){
console.log( this.props.location.state.location) // Cannot read property 'props' of undefined
return "test"
}
Cannot read property 'props' of undefined
I tried also:
function Details({survey){
console.log(survey) // Undefined
return "test"
}
and
function Details({ location: survey }){
console.log(survey) // Undefined
return "test"
}
I am really not understanding what I am doing wrong. I am following the documentations:
https://reacttraining.com/react-router/web/guides/primary-components
this is not accessable in functional components. You need to do it like this:
import { useHistory } from "react-router-dom";
function Details(props){
const history = useHistory();
console.log(history.location.state.location);
return <p>Test</p>
}
export default Details;
Hope this works for you.
To connect your component to the routes information, you should use the withRouter HOC or useHistory hook.
withRouter example:
import { withRouter } from "react-router";
function Details(props){
console.log(props.history.location.state.location);
return <p>Test</p>
}
export default withRouter(Details);
I have two functional components in my process. First, I need the first component info to be filled and validated by Formik and Yup and then user can process the next step in the second component by click Next. For now, I can get everything validated and code can reach on handleSubmit() without any problem. But, the problem is that, I could not link to another component using <Link>. I have tried:
// Using this first one, no validation is performed and it will link to another component directly
<Link to="/Next">
<button type="submit">Next</Button>
</Link>
// I have put these inside handleSubmit() but it is undefined.
this.context.router.push('/Next');
Router.push('/Next')
this.props.history.push('/Next')
Mostly I got undefined output on console using these code. It's seems like that i could not access props from functional components like i could in the react class. Here is my first component:
import React from 'react';
import { withFormik, Field } from 'formik';
import {
BrowserRouter as Router,
Route,
Link,
Switch,
Redirect
} from "react-router-dom";
const MyForm = props => { const {handleSubmit} = props;
return (
<form onSubmit={handleSubmit}>
<Field type="email" name="email" placeholder="Email" />
<button type="submit">Next</button>
// <Link to="/Next">
// <button type="submit">Next</Button>
// </Link>
</Field>
);
};
const MyEnhancedForm = withFormik({
mapPropsToValues: () => ({ email: '' }),
handleSubmit: (values, formikBag) => {
// Link to next page code
},
})(MyForm);
Yes we can't use this.props in functional components but what we can do is that write the same routing logic in parent component and pass it as a prop in the functional component.
Then we can call the same function in handleSubmit().
If you are using hooks you can follow the approach described in this blog post https://dev.to/httpjunkie/programmatically-redirect-in-react-with-react-router-and-hooks-3hej in order to programmatically redirect with react-router and hooks. A summary:
Include useState in your imports:
import React, { useState } from 'react';
Inside your functional component add:
const [toNext, setToNext] = useState(false)
Inside handleSubmit add:
setToNext(true)
Inside the <form> add:
{toNext ? <Redirect to="/Next" /> : null}