I'm trying to make a button that only redirects the user to a new page after validation is completed correctly.
Is there a way of doing something like this?
How to I get a Route to be activated inside of a class method?
import validator from './validator';
class Example {
constructor(props) {
super(props)
this.saveAndContinue = thos.saveAndContinue.bind(this)
}
saveAndContinue () {
var valid = validator.validate(this.props.form)
if (valid) {
axios.post('/path')
<Redirect to='/other_tab'>
} else {
validator.showErrors()
}
}
render() {
<button onClick={this.saveAndContinue}>Save and Continue</button>
}
}
As discussed you should have access to the history object via this.props.history as described here.
If you look into the push function this will redirect you to any route you need.
For example:
// Use push, replace, and go to navigate around.
this.props.history.push('/home', { some: 'state' })
https://reacttraining.com/react-router/web/api/history
Like Purgatory said you can do it without using <Redirect />, but otherwise you could make your component a stateful component and then do a conditional render like so
render() {
!this.state.redirect ?
<button onClick={this.saveAndContinue}>Save and Continue</button> :
<Redirect to='/other_tab'>
}
And let the saveAndContinue() change the component state.
saveAndContinue () {
var valid = validator.validate(this.props.form)
if (valid) {
axios.post('/path')
this.setState({redirect: true});
} else {
validator.showErrors()
}
}
When the state changes it would cause a re-render and this time the <Redirect /> would be rendered.
Note: I didn't actually run this code snippet, so it may contain (hopefully minor) errors.
Related
i 'm traying to use Redirect from react-router-dom 4.3.1.
All the logic i apply into mi app.js and i do a get for the token and if exist i have to redirect the user to the homepage..
Also i try to use history, but i dosn 't work.. where is the problem?
app.js
import { Redirect } from 'react-router-dom'
async componentDidMount() {
try{
const urlToken = new URL(window.location)
const authToken = urlToken.searchParams.get('auth_token')
console.log('MATCH ',authToken)
const tokenUser = await getTokenUser(authToken)
console.log('tokenUser', tokenUser.status)
if(tokenUser.status == 200) {
this.setState({ toHome: true })
}
}catch(error){console.log('NOT')}
}
render(){
if(this.state.toHome == true) {
return <Redirect to='/dashboard' />
}
}
The purpose of componentDidMount is not to return a React element. If you want to redirect the user here, you will have to do it programmatically.
One thing that you have to remember is that the Redirect should be part of the returned JSX in your render method.
You have to create a render method in your component. You can return in also conditionally if you want to check something before redirecting. It would look something like:
render () {
return (
<div>
{(condition) && <Redirect />}
</div>)
I'm New to React.js, trying to build form validation.
There is Btn component.
const MyBtn = ({opts}) => {
if(!opts) {
opts = {};
}
return (
<button {...opts}}>{opts.value}</button>
)
}
There is Home.js which is a login page. I don't add other codes that you might not need for this problem.
class Home extends Component {
state = {
formValid: false
}
regexCheck = {
disabled : {this.state.formValid},
value: "LogIn",
type: "submit"
}
render(){
return(
<MyBtn opts={this.regexCheck}/>
)
}
}
//output that I'm trying to get
<button type="submit" disabled={this.state.formValid}>LogIn</button>
I get an error that I can't call {this.state.formValid} in the props. since this is a reserved word. (Sorry for my bad English.) I feel like I'm totally wrong.
Anyway, I thought I can use getDerivedStateFromProps method for this...? so I added this in Home.js but didnt work.
static getDerivedStateFromProps(nextProps, prevState){
if(prevState.formValid !== nextProps.formValid){
return { formValid: nextProps.formValid };
}
return null;
}
As you can see, I'm still confused by props and state. ahhhh but I still gotta keep doing this project. Please give me any idea to solve this.
This is invalid syntax:
disabled : {this.state.formValid},
You simply want:
disabled : this.state.formValid,
I am using React and Redux for a search application. Using react-router-dom, I'm routing /search/:term? to a Search component:
<Router>
<Switch>
<Route exact path="/search/:term?" component={Search} />
<Redirect to="/search" />
</Switch>
const Search = (props) => {
const { term } = props.match.params;
return (
<div>
<SearchForm term={term}/>
<SearchResults />
</div>
)
};
When a user submits a search in the SearchForm component, I'm dispatching an action to submit the search query. I'm also initiating a search in the constructor if a term is given, initially:
class SearchForm extends Component {
constructor(props) {
super(props);
const term = props.term ? props.term : '';
this.state = {
term: term,
}
if (term) {
this.props.submitSearch(term);
}
}
handleSubmit = (e) => {
e.preventDefault();
if (this.state.term) {
this.props.submitSearch(this.state.term);
}
}
render = () => {
<form
onSubmit={this.handleSubmit.bind(this)}>
...
</form>
}
}
I'm using withRouter from react-router-dom, so the URL updates when the search is submitted.
The problem happens when the user navigates Back in their browser. The URL navigates back, the props update (i.e. props.match.params.term), but the search does not resubmit. This is because the submitSearch action only gets dispatched in SearchForm.constructor (search on initial loading if a term is in the URL) and SearchForm.handleSubmit.
What is the best way to listen for a state change to term when the URL changes, then dispatch the search action?
I would retrieve the route parameter in componentDidMount since you are pushing a new route and therefore reloading the view.
Inside your SearchForm it would look like this.
state = {
term: '';
}
onChange = (term) => this.setState({ term })
onSubmit = () => this.props.history.push(`/search/${this.state.term}`);
And in your SearchResult :
componentDidMount() {
this.props.fetchResults(this.props.term)
}
A nice thing to do would be to keep the SearchResult component dry. There are several ways to achieve that, here is one using higher order components aka HOC :
export default FetchResultsHoc(Component) => {
#connect(state => ({ results: state.searchResults }))
class FetchResults extends React.Component {
componentDidMount(){
dispatch(fetchResults(this.props.match.params.term))
}
render(){
<Component {...this.props} />
}
}
return FetchResultsHoc;
}
That you would then call on your SearchResult component using a decorator.
import { fetchResults } from './FetchResultsHoc';
#fetchResults
export default class SearchResult extends React.PureComponent { ... }
// You have now access to this.props.results inside your class
My current solution is to dispatch submitSearch in the componentWillRecieveProps lifecycle method if the new props don't match the current props:
componentWillReceiveProps(nextProps) {
if (this.props.term !== nextProps.term) {
this.setState({
term: nextProps.term,
});
this.props.submitSearch(nextProps.term);
}
}
Then, instead of dispatching an action on form submission, I push a new location onto the history and componentWillReceiveProps does the dispatching:
handleSubmit = (e) => {
e.preventDefault();
if (this.state.term) {
this.props.history.push('/search/'+this.state.term);
}
}
This solution feels a little wrong, but it works. (Other's would seem to agree: Evil things you do with redux — dispatch in updating lifecycle methods)
Am I violating a React or Redux principle by doing this? Can I do better?
Say I have the following route defined in a Switch:
<Switch>
<Route path='/:pageId' component={Template} />
<Switch>
In the template I pass :pageId to the API and it returns the content for that page. It all works well, however the app crashes if I pass a :pageId value that does no exists (like I have no page with a slug "contact").
How can I make it redirect to a 404 page in this case so as to avoid the app crashing?
Thanks in advance.
Since you're just passing a variable page id to the Route component and not specifically naming each route, you'll want to have the Template component return a 404 page in the case where the server returns nothing.
Switch will take a fall-through component in the case where none of the available paths match the given path, but that only works in case where you're using specific, named paths, ie /users, not with one route linking to variable page names.
A possible approach is to utilize React 16 error boundaries. Then you can simply throw whenever (as long as it is inside a render method IIRC) you know the route is invalid.
class RouteNotFoundError extends Error {
constructor(...args) {
super(...args)
// extending Error is fickle in a transpiler world - use name check instead of instanceof/constructor
this.name = "RouteNotFoundError"
}
}
const RouteNotFoundBoundary = withRouter(
class RouteNotFoundBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { routeNotFound: undefined }
}
componentWillReceiveProps(nextProps) {
if (this.props.location !== nextProps.location) {
this.setState({routeNotFound: false})
}
}
componentDidCatch(e) {
if (e.name === "RouteNotFoundError") {
this.setState({routeNotFound: this.props.location.key})
} else {
throw e
}
}
render() {
if (this.state.routeNotFound === this.props.location.key) {
return this.props.fallback || <div>Not found!</div>
} else {
return this.props.children
}
}
}
)
const Template = props => {
if (!allMyPages[props.match.params.pageId]) {
throw new RouteNotFoundError()
}
return renderMyPage(allMyPages[props.match.params.pageId])
}
const Example = () => (
<RouteNotFoundBoundary fallback={<div>Oh no!</div>}>
<Switch>
<Route path='/:pageId' component={Template}/>
</Switch>
</RouteNotFoundBoundary>
)
Not sure if it's a good idea or not, but it might simplify some cases when the code path where you know if the route is valid or not is not the best place to render a 404 page.
I want to navigate from one component to another ,as in, upon a button click in component1 , we should be redirected to component2 without having to use this.navigation.navigate() as this requires specifying a new URL. I want the URL to remain same and I do not want to use react-router. Is there a way to do this?
The behavior ideally is that a button click in component1 will render component2 in the place of component1 which takes up the whole screen. In a way component1 would cause component2 to render. But I do not want the user to have access to component2 directly. Hence I do not want a change in the URL. Would containers still be the way to go?
You need to return that component witch appropriate by your condition like this:
class Container extends Component {
render() {
let cond = this.props.condition
return (
{ cond ? <Component1/> : <Component2/> }
)
}
}
EDIT: switch may be like this:
class Component1 extends Component {
render() {
return (
<button onClick={this.props.onDone}/>
)
}
}
class Container extends Component {
constructor(props) {
this.state = { done: false }
}
render() {
let cond = this.state.done
return (
{ cond ?
<Component1 onDone={()=>this.setState({done:true})}/> :
<Component2/>
}
)
}
}