Navigate to another page using Material SearchBar - reactjs

I am using a Material SearchBar in the header of my app, and trying to navigate to a page '/search' on localhost onRequestSearch. The code for the search bar itself is simple:
<SearchBar
onChange = {() => console.log('onChange')}
onRequestSearch = {() => this.setRedirect()}
style = {{ margin: '0 auto', maxWidth:800}}/>
Now, I've tried implementing setRedirect function in a number of ways with no luck. First, I simply tried:
setRedirect() {
this.props.history.push('/search');
}
But this gives an error of Undefined is not an object (evaluating this.props.history). I do have a Route set up for "/search" page in my main app.
I also tried using Redirect:
constructor(props) {
super(props);
this.state = {
redirect = false
}
}
setRedirect() {
this.setState({
redirect: true
}
}
And in the render method:
render() {
if(this.state.redirect) {
return (
<Router>
<div>
<Redirect push to="/search"/>
<Route path="/search" exact strict component={Search}/>
</div>
</Router>
);
}
else {
// return other stuff
}
}
This simply results in the entire page going blank when I type something in the search bar and hit enter. The address in the browser does NOT change to '/search' either.
Not sure what I'm doing wrong. Simply trying to navigate to another page once user enters text in the search bar and hits enter. Any suggestions will be greatly appreciated. Thank you in advance!

Try to add withRouter HoC and then try again with this.props.history.push()
import { withRouter } from 'react-router-dom'
...
export default withRouter(MyComponent);

Related

How to use "<Redirect>" inside a function?

This is my function so that when a user logs in, it checks the user's password and username and if they are both correct it should redirect to the /note url with the user ID passed through it. I have seen people use it on render(), but I would like to keep my App.jsx app simple, I think that's good practice.
I have tried using the useHistory method, but when the user inputs a data, the user needs to refresh the page manually for the new data to show. If I add a useHistory to refresh the page, it says that the location.state value is undefined.
How should I go about this problem?
Thank you
// Checking to see if current user's log info exists in db
function logIn(e) {
e.preventDefault();
const currentUser = users.find(
(users) => users.username === userInput.username
);
if (!currentUser) {
console.log("The Username was not found. Please try again");
} else {
if (currentUser.password === userInput.password) {
// history.push("./note", { userId: currentUser._id });
return <Redirect to={"/note/" + currentUser._id} />;
} else {
console.log("The password is incorrect");
}
}
}
Here's my routing code
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import NoteDashboard from "./NoteDashboard";
import Home from "./Home";
import CreateAccount from "./CreateAccount";
function App() {
return (
<Router>
<div className="container">
<Switch>
<Route exact path="/" component={Home}></Route>
<Route path="/create" component={CreateAccount}></Route>
<Route path="/note" component={NoteDashboard}></Route>
</Switch>
</div>
</Router>
);
}
export default App;
The only thing (I found) you can do to redirect to a page from function is use ReactDOM.hydrate. Here an example. Example works, but only first time. After that, redirection stops to works. Maybe because codesandbox concats to url the symbol # for some technical reason that I don't know. Try to apply this solution locally an let me know if works more tha one time.
Okay I got it to work, but I don't know if this is good practice or not.
Inside the if statement where I check to see that it is the correct user, I call a function which changes a React Hook from false to true, then in my return statement, I say
{redirect ? <Redirect to={"/note/" + currentUserId} /> : null}
It seems to work fine, everything works. Does this look like a good solution or are there some flaws that I am not seeing.
Thank you for all the help.
try something like this:
state = {
username: '',
app_pass: '',
redirectLink: '',
redirect: false
};
componentDidMount() {
if (window.localStorage.getItem('access_token')) {
this.setState({ redirectLink: '/user/dashboard', redirect: true });
}
}
render() {
const { redirect } = this.state;
const { redirectLink } = this.state;
if (redirect) {
return <Redirect to={redirectLink} />;
}
}

Can 't redirect to homepage after login whit <Redirect>

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>)

React Router how to return 404 page in case of dynamic routes expecting a parameter?

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.

React Router Redirect Conditional

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.

How to reload a component in reacts on url parameter change?

I have the following route:-
ReactDOM.render((
<Router history={browserHistory}>
<Route path="/" component={app}>
<IndexRoute component={home}/>
<Route path="/articles" component={articles} />
<Route path="/articles/topics/:featureName" component={filteredArticles} />
</Route>
</Router>
), document.getElementById('app'));
Now when i request for /articles/topics/topic1, the first 5 contents are fetched are using API call. When user reaches the bottom of the page again an API call is made where next 5 contents are fetched. This is working fine(Notice that the handlescroll function checks for the page bottom, which has few minor issues so commented that out for now.)
Now say I choose next topic from the list so the url changes, so my url changes to /articles/topics/topic2 , now i need the same thing as mentioned above and call apis for this topic.
Following is my current component code:-
import React from 'react';
import axios from 'axios';
import ArticleList from './ArticleList';
import Banner from './Banner';
import LeftNavBar from './LeftNavBar';
import RightNavbar from './RightNavbar';
import {url} from './Constants';
/*MERGE INTO ARTICLES ONCE SCROLL LOGIC IS FIXED*/
class FilteredArticles extends React.Component{
constructor(){
super();
{/* Adding global event listener,binding it to scroll event and adding it to handleScroll function */}
/*window.addEventListener("scroll", this._handleScroll.bind(this));*/
this.state ={
articles : [],
loadingFlag:false,
limit : 5,
offset :0,
subFeatureName:[],
flag: false
}
}
_getContent(){
let self=this;
axios.get(url+'/articles/filter/?filter=popular&category='+this.props.params.featureName+'&limit='+this.state.limit+'&offset='+this.state.offset)
.then(function(response){
/*Checking if limit has exceeded meta count, if it has stopped we dont call the APIS*/
if(self.state.limit >= response.data.meta.total_count){
self.setState({articles : self.state.articles.concat(response.data.results),
offset : self.state.limit,
limit: self.state.limit+self.state.offset,
subFeatureName: self.props.params.featureName,
loadingFlag: false}
)
}
else{
self.setState({articles : self.state.articles.concat(response.data.results),
offset : self.state.limit,
limit: self.state.limit+self.state.offset,
subFeatureName: self.props.params.featureName,
loadingFlag: true}
)
}
})
.catch(function(error){
console.log(error);
});
}
_handleScroll(){
/*Calling 5 more articles when someone scrolls down and reaches the page end
const windowHeight = window.innerHeight;
const scrollT = $(window).scrollTop();
if(windowHeight- scrollT < 200){
if(!this.state.loadingFlag){
this.setState({loadingFlag : true});
this._getContent();
}
}*/
}
componentDidMount(){
/*calling _getContent function to get first five articles on page load*/
console.log("got content");
this._getContent();
}
render(){
console.log("inside render");
return(
<div>
<Banner title="Article Page"/>
<div className="content-container">
<LeftNavBar/>
<div className ="feeds">
<div className="card">
{/* Sending hideSeeAll as prop to remove see all button on main article page*/}
<ArticleList articles={this.state.articles} hideSeeAll='true' keyword="Top Articles"/>
</div>
</div>
<RightNavbar/>
</div>
</div>
)
}
}
export default FilteredArticles;
So basically I am trying to understand that if I have a route like /articles/topics/:featureName and I call a page like /articles/topics/topic1 or /articles/topics/topic2, can the component be realoaded(I need to call different API are fetch content once again).
That is, for /articles/topics/topic1 the API called is
/api/v0/articles/filter/?filter=popular&category=topic1&limit=5&offset=0
and for /articles/topics/topic2 the API called is
/api/v0/articles/filter/?filter=popular&category=topic2&limit=5&offset=0
Is there any way to achieve this?
You shouldn't really be querying the API/updating the components state in this fashion but hey that's another question entirely. Maybe consider using a Flux approach.
Anyhow, you can use the componentWillReceiveProps(nextProps) life cycle method to receive the routers nextProps params and query your API again:
componentWillReceiveProps(nextProps) {
this._getContent(nextProps.params.featureName);
}
_getContent(featureName) {
...query your API
}
Also, if you're using Redux, make a check like this to avoid an infinite loop:
componentWillReceiveProps(newProps) {
if (this.props.someValue === newProps.someValue) {
this.props.fetchSomething(newProps.params.somethingName);
}
}

Resources