Can I use ref.current.setState(...) - javascript

I have a situation that want to call a Child.method from Parent, though this is not a best practice, but I just want to give it a try. And I decide to use ref.current.setState() in the Parent component.
Here is the example code.
https://codesandbox.io/s/8lmvq3yq68
There are some unexpected behavior happened where ref, setState and react-router-dom are used together. When I use Redirect and ref together, the Child.componentDidUpdate will not be called. I wonder if it is a valid code in React? Since I can not find any doc that showing it is invalid. Or is it a bug of react-router-dom?

I logged in some additional component lifecycle logs to clarify.
So what's happening is when you select the 'Link' to '/' There are 2 parallel calls that will fire.
Navigate to the '/' route
The onClick event
So one will first navigate to the relevant route while the other will trigger the ref's function. You will see the "Calling the state change" log.
What happens when you route to the '/' path is that the SuperHero
component is unmounted! And you will see the "Unmounting - Superhero"
log. So the state change is lost and componentDidUpdate will not be fired. Nevertheless, as you can see, the redirect does happen
and the component is mounted again. Now it has no sense of the state
change but the "Mounting - Superhero" will be logged.
I have further included a separate button. Since there is no unmounting, this works as you expect!
https://codesandbox.io/s/81v0mz0548
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Link,
Redirect,
Route
} from "react-router-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.superheroElement = React.createRef();
}
handleClick = () => {
this.superheroElement.current.changeName();
};
render() {
return (
<Router>
<Link to={"/"} onClick={this.handleClick}>
haha
</Link>
<Route path="/" exact={true} render={() => <Redirect to="/post" />} />
<Route
path="/post"
render={props => <Superhero ref={this.superheroElement} {...props} />}
/>
<br/>
<button onClick={this.handleClick}>
haha
</button>
</Router>
);
}
}
class Superhero extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "Batman"
};
this.changeName = this.changeName.bind(this);
}
changeName() {
console.log("Calling the state change");
this.setState({
name: "Bruce Wayne"
});
}
componentDidUpdate() {
// Not called because component is unmounted
console.log("Updating - Superhero");
}
componentDidMount() {
console.log("Mounting - Superhero");
}
componentWillUnmount() {
console.log("Unmounting - Superhero");
}
render() {
return <div>{this.state.name}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Related

How to render state directly after setting it?

Introduction: I am very new to ReactJS(I just started a week ago), but I am not using that as an excuse, so please go hard on me, if there is something that I am not understanding.
Abstract: I am trying to implement Protected Routes.
Background: Upon the Protected Route component mounting, componentDidMount() invokes a function called isAuthenticated() that changes the state of a field called, isAuthenticated. That same field is what I am checking for to determine whether the user sees the Protected Component or is routed back to the Login page.
Issue: Everytime I visit the Protected Route aka CreatePost, my logs show that isAuthenticated() is invoked after componentDidMount() but I am unsure why my UI is not reflecting those new changes, to show the user the authenticated route.
Question: Can anyone please assist or recommend a better strategy that I have not considered? I really appreciate it.
Note: If I declare the this.state.isAuthenticated field in my constructor to be true, I will see my Protected Route, but this is not my intended goal. Hope this is helpful in diagnosing the issue.
App.js:
import React, {Component} from 'react'
import './App.css'
import Signup from './components/Signup'
import Login from './components/Login'
import Feed from './components/Feed'
import CreatePost from './components/Createpost'
import ProtectedRoute from './components/Protectedroute'
import {
BrowserRouter as Router,
Switch,
Link,
Route,
Redirect
} from 'react-router-dom'
class App extends Component {
render() {
return(
<Router>
<div>
<Link to ='/signup'>Signup</Link>
<br />
<Link to = '/login'>Login</Link>
<br />
<Link to ='/create-post'>Create Post</Link>
<br />
<Link to ='/feed'>Feed</Link>
</div>
<Switch>
<Route path = '/signup' component = {Signup} />
<Route path = '/login' component = {Login} />
<Route path = '/feed' component = {Feed} />
<ProtectedRoute path = '/create-post' component = {CreatePost} />
</Switch>
</Router>
)
}
}
export default App
ProtectedRoute.js
import React, {Component} from 'react'
import { Redirect } from 'react-router-dom'
class ProtectedRoute extends Component {
constructor(props) {
super(props)
this.state = {
isAuthenticated: false
}
this.isAuthenticated = this.isAuthenticated.bind(this)
}
componentDidMount() {
console.log('--componentDidMount--')
this.isAuthenticated()
}
isAuthenticated() {
console.log('--isAuthenticated--')
this.setState({isAuthenticated: true})
}
render() {
const Component = this.props.component
return this.state.isAuthenticated ? (<Component />) : (<Redirect to = '/login' />)
}
}
export default ProtectedRoute
Instead of setting state inside the component for that api call try creating a function and then returning the api response as true or success
import React, { Component } from "react";
import { Redirect, Route } from "react-router-dom";
class ProtectedRoute extends Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: false
};
this.isAuthenticated = this.isAuthenticated.bind(this);
}
componentDidMount() {
console.log("--componentDidMount--");
this.isAuthenticated();
}
isAuthenticated() {
console.log("--isAuthenticated--");
// Authentiction logic
return true;
}
render() {
const Component = this.props.component;
return (
<Route
render={props =>
this.isAuthenticated() ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);
}
}
export default ProtectedRoute;
Seems like the problem here that setState is async. What happens is that in the first render this.state.isAuthenticated is false and therefore immediately redirecting. You can introduce a loading state which will help you defer the rendering of the component \ redirecting to only after the state of both loading and isAuthenticated are settled.
In general never expect setState to change the state immediately in a synchronous way.

Pass props or data beteween components React

Good afternoon friends,
My pages and components are arranged in the main class of my application, can I pass some results from any component or page to the main class and get this property from main class to any other component.
To describe question well I will show an example:
This is my main class App.js
import React, {Component} from 'react';
import { BrowserRouter as Router, Route} from "react-router-dom";
import HomePage from "./Pages/HomePage";
import NavBar from "./Components/NavBar";
import PaymentStatus from "./Pages/PaymentStatus";
class App extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
render() {
return (
<Router>
<NavBar/>
<Route name={'Home'} exact path={'/'} component={HomePage}/>
<Route name={'PaymentStatus'} exact path={'/payment-status/:tId'} component={PaymentStatus}/>
</Router>
);
}
}
export default App;
Now my navigation bar component: NavBar.js
import React, {Component} from 'react';
class NavBar extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
_makeSomething =async() => {
// Somw function that returns something
}
render() {
return (
<div>
<div id={"myNavbar"}>
<div>
<a onClick={()=>{this._makeSomething()}} href={'/'}/> Home</a>
<a onClick={()=>{this._makeSomething()}} href={"/payment-status"} />Payment Status</a>
</div>
</div>
);
}
}
export default NavBar;
HomePage.js
import React, {Component} from 'react';
class HomePage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
async componentDidMount() {
console.log(this.props.match.params.tId)
};
render() {
return (
<div>
<div id={"main"}>
<div>
<p>This is home page</p>
</div>
</div>
);
}
}
export default HomePage;
PaymentStatusPage.js
import React, {Component} from 'react';
class PaymentStatusPage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
async componentDidMount() {
console.log(this.props.match.params.tId)
};
render() {
return (
<div>
<div id={"status"}>
<div>
<p>This is payment Status Page</p>
</div>
</div>
);
}
}
export default PaymentStatusPage;
Now here is the question:
Can I pass to App.js events (or props) when HomePage.js or PaymentStatusPage.js or when something was changed in NavBar.js
Also, want pass received peprops to any component.
Thank you.
You can decalare method in class App and then pass it to another component via props.
For example
Then you can call this method in MyComponent and pass some value to it. This is the way you pass value from subcomponent to parent component. In method in App you can simply use setState.
What's left to do is to pass this new state attribute to another component via props.
To pass value to component, while using you have to change
<Route component={SomeComponent}
To
<Route render={() => <SomeComponent somethingChanged={this.somethingChangedMethodInAppClass}}/>
Hope it helps!
EDIT: You can also use Redux to externalize state and reuse it in child components
You have two options here:
Keep all of your state in your parent component, App, and pass any props down to your children component, even actions that could update the parent state. If another children uses that state, then that child will be rerendered too.
Manage your state with Redux and make it available for all your components.
I created a small example out of your scenario.
In this example, the App component has a state with a property called title and a function that is passed down via props to the Navbar.
class App extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {
title: "Home Page"
};
}
_makeSomething = title => {
this.setState({ title: title });
};
render() {
return (
<Router>
<NavBar clicked={this._makeSomething} />
<Route
name={"Home"}
exact
path={"/"}
component={() => <HomePage title={this.state.title} />}
/>
<Route
name={"PaymentStatus"}
exact
path={"/payment-status/:tId"}
component={() => <PaymentStatus title={this.state.title} />}
/>
</Router>
);
}
}
The components HomePage and PaymentStatus will get that title as props from the App's state and NavBar will get the _makeSomething function as props. So far, all that function does is update the state's title.
class NavBar extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
}
render() {
return (
<div>
<div id={"myNavbar"}>
<NavLink
onClick={() => {
this.props.clicked("Home Page");
}}
to={"/"}
>
{" "}
Home
</NavLink>
<NavLink
onClick={() => {
this.props.clicked("Payment Page");
}}
to={"/payment-status/1"}
>
Payment Status
</NavLink>
</div>
</div>
);
}
}
In the Navbar, when the function I passed down from App as props is clicked, it will go all the way back to the App component again and run _makeSomething, which will change the App's title.
In the mantime, the components HomePage and PaymentStatus received title as props, so when the state's title is changed, these two children component will change too, since their render function relies on this.props.title.
For example, HomePage:
class HomePage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
}
async componentDidMount() {
console.log(this.props.match.params.tId);
}
render() {
return (
<div>
<div id={"main"}>
<p>This is {this.props.title}</p>
</div>
</div>
);
}
}
Like I said before, by keeping your state in the parent component and sending down to the children component just what they need, you should be able to accomplish what you need.
A note: I did change the anchor tag from <a> to NavLink which is what you're supposed to use with react-router-dom if you don't want a complete refresh of the page.
The full code can be found here:
Have a look at Context. With this you can pass an object from a Provider to a Consumerand even override properties with nested providers: https://reactjs.org/docs/context.html
AppContext.js
export const AppContext = React.createContext({})
App.js
someFunction = ()=>{
//implement it
}
render() {
const appContext = {
someFunction: this.someFunction
}
return (
<AppContext.Provider value={appContext}>
<Router>
<NavBar/>
<Route name={'Home'} exact path={'/'} component={HomePage}/>
<Route name={'PaymentStatus'} exact path={'/payment-status/:tId'} component={PaymentStatus}/>
</Router>
</AppContext>
);
}
Homepage.js
class HomePage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
async componentDidMount() {
console.log(this.props.match.params.tId)
this.props.appContext.someFunction(); //calls the function of the App-component
};
render() {
return (
<div>
<div id={"main"}>
<div>
<p>This is home page</p>
</div>
</div>
);
}
}
export default (props) => (
<AppContext.Consumer>
{(appContext)=>(
<HomePage {...props} appContext={appContext}/>
)}
</AppContext.Consumer>
)
You can also use this mechanic with function components. I'm normally encapsulating the Consumer to an extra component. So all values available for the component as normal property and not just inside the rendered components.

I have a problem with redirection in React

I am new to react and i am creating a simple Contact Manager App, i have contacts and addContact components, these are separated in different routes.
When i add a contact,it gets added to the local storage and the contacts component is getting the list of contacts from the local storage.
The problem that when i add a contact and i redirect to the contacts page, the new contact doesn't show unless i manually refresh the page.
i tried this :
this.props.history.push('/');
this too
window.location.href=window.location.hostname;
and i created a function to refresh the page if the previous link is the addContact link, it doesn't work either
this is the App component code:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Navbar from './components/navbar';
import Contacts from './components/contacts';
import About from './components/pages/about';
import AddContact from './components/pages/addContact'
import NotFound from './components/pages/notFound'
function getContactsLS() {
let contacts;
if (localStorage.getItem('contacts') === null) {
contacts = [];
}
else {
contacts = JSON.parse(localStorage.getItem('contacts'));
}
contacts = Array.from(contacts);
return contacts;
}
class App extends React.Component {
contactsLS = getContactsLS();
render() {
return (
<Router>
<Navbar />
<div className="container">
<Switch>
<Route exact path="/add-contact" component={AddContact} />
<Route exact path="/" component={() => <Contacts contacts={this.contactsLS} />} />
<Route exact path="/about" component={About} />
<Route component={NotFound}/>
</Switch>
</div>
</Router>
)
}
}
export default App
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
this is addContact function :
addContactEvent = () =>{
let contacts;
if(localStorage.getItem('contacts')===null)
contacts=[];
else{
contacts=JSON.parse(localStorage.getItem('contacts'));
}
contacts=Array.from(contacts);
console.log(contacts);
this.setState({key:contacts.length});
const contact = {
key : contacts.length,
name: this.state.name,
email:this.state.email,
phone:this.state.phone
}
contacts.push(contact);
localStorage.setItem('contacts', JSON.stringify(contacts));
//redirection
//this.props.history.push('/');
window.location.href=window.location.hostname;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
It feels like you would want the contacts as part of the state. In that case the code would be:
state = { contactLS: getContactsLS()}
But as we are calling values from localStorage a more appropriate place might be
componentDidMount() method.From react docs
Avoid introducing any side-effects or subscriptions in the
constructor. For those use cases, use componentDidMount() instead.
Then the code would be
componentDidMount(){
const contacts = getContactsLS()
this.setState({ contacsLS: contacts })
}
Don't forget to change to this.state.contactsLS
<Route exact path="/" component={() => <Contacts contacts={this.state.contactsLS} />} />
====
PS
Another problem i can see in the existing code is the misuse of component constructor.
This is not obvious but the line
contactsLS = getContactsLS();
is equivalent to
constrcutor(){
this.contactLS = getContactLS();
}
from react documentation we read
Typically, in React constructors are only used for two purposes:
- Initializing local state by assigning an object to this.state.
- Binding
event handler methods to an instance.
You could use <Redirect> component from react-router-dom package. To get it working you initially setState where redirect is set to false.
this.state = {
redirect: false
}
Once the addContact opeartion is done, update the state
this.setState({
redirect: true
})
Inside of your AddContact component, the state will decide if it has to show the actual component or to redirect to the attached path.
Something like this:
import {Redirect } from 'react-router-dom';
class AddContact extends React.Component {
//Add the initial state
// addContact Operation
render(){
if(this.state.redirect)
return <Redirect to="/" />
return (
//The markup for AddContact
)
}
}

How would I restructure my React App, so that I can pass state down to a component on a different route

I have three components routed to different paths. I want to restructure my App so that I can pass state via props from my SubmitProject Component to my Portfolio Component I still want them to have separate paths ie; /portfolio and /SubmitProject I plan to have two browserwindows open to test that when I submit a form on SubmitProject it will show up on Portfolio then I will be using firebase to persist my state to a database.
Do I need to have my state be at a top level Component like App.js and then have the BrowserRouter inside of that? If so how do I recreate the connections I have made from <SubmitProject/> -> <PortfolioForm/> -> <FormAdd/>
My Desired Goal is that when I submit the form from the FormAdd Component when I am on the /submit Route that it will output via state on my Portfolio Component on the /Portfolio Route.
It has been recommend to use a state manager like context api, or something else, but I want to know if there is a way to restructure my App and be able to pass state from a top level component that each component and route share.
Here is my relevant code
components/Router.js
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Portfolio from './Portfolio';
import SubmitProject from './SubmitProject';
import App from './App';
const Router = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={App}/>
<Route exact path="/portfolio" component={Portfolio}/>
<Route exact path="/submit" component={SubmitProject}/>
</Switch>
</BrowserRouter>
);
export default Router;
components/App.js // Should My Router be in here?
import React from 'react';
class App extends React.Component {
render() {
return <div>Test</div>
}
}
export default App;
/components/SubmitProject.js
import React from 'react';
import PortfolioForm from './PortfolioForm';
import Section from './Section';
class SubmitProject extends React.Component {
state = {
sections:{}
};
addSection = section =>{
const sections = {...this.state.sections};
sections[`section${Date.now()}`] = section;
this.setState({
sections: sections
});
}
render() {
return(
<React.Fragment>
<h1>Submit Project</h1>
<h2>Enter Project Data</h2>
<ul className="section">
{Object.keys(this.state.sections).map(key => <Section key={key} details={this.state.sections[key]}/>)}
</ul>
<PortfolioForm addSection={this.addSection} />
</React.Fragment>
)
}
}
export default SubmitProject;
/components/PortfolioForm.js
import React from 'react';
import FormAdd from './FormAdd';
class Portfolio extends React.Component {
render() {
return(
<React.Fragment>
<h1>Submit Form</h1>
<FormAdd addSection={this.props.addSection}/>
</React.Fragment>
)
}
}
export default Portfolio;
/components/FormAdd.js
import React from 'react';
class FormAdd extends React.Component {
nameRef = React.createRef();
createSection = (event) =>{
event.preventDefault();
const section = {
name: this.nameRef.current.value
};
this.props.addSection(section);
};
render() {
return(
<React.Fragment>
<form onSubmit={this.createSection}>
<input type="text" ref={this.nameRef} name="name" placeholder="Name"/>
<button type="submit">+ Add Section</button>
</form>
</React.Fragment>
)
}
}
export default FormAdd;
/components/Portfolio.js
import React from 'react';
class Portfolio extends React.Component {
//CAN I GET STATE FROM SubmitProject.js FILE IN HERE? By Restructuring my App Somehow.
render() {
return(
<React.Fragment>
<h1>Portfolio Page</h1>
<h2>List of projects</h2>
</React.Fragment>
)
}
}
export default Portfolio;
UPDATED CODE
I am now getting an error that says FooContext is not defined
components/App.js
import React from 'react';
import SubmitProject from './SubmitProject';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
const FooContext = React.createContext();
class App extends React.Component {
state = {
sections:{}
};
addSection = section =>{
const sections = {...this.state.sections};
sections[`section${Date.now()}`] = section;
this.setState({
sections: sections
});
}
render() {
return (
<FooContext.Provider value={this.state.sections}>
<Router/>;
</FooContext.Provider>
)
}
}
class Router extends React.PureComponent {
render() {
return
<BrowserRouter>
<Switch>
<Route exact path="/" component={Root} />
</Switch>
</BrowserRouter>
}
}
const Root = props => <FooContext.Consumer>{sections => <SubmitProject/> }</FooContext.Consumer>;
export default App;
UPDATED CODE V#2
App.js
import React from 'react';
import SubmitProject from './SubmitProject';
import Home from './Home';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
const FooContext = React.createContext();
class App extends React.Component {
state = {
sections:{}
};
addSection = section =>{
const sections = {...this.state.sections};
sections[`section${Date.now()}`] = section;
this.setState({
sections: sections
});
}
render() {
return (
<FooContext.Provider value={this.state.sections}>
<Router/>;
</FooContext.Provider>
)
}
}
class Router extends React.PureComponent {
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/portfolio" component={Portfolio} />
</Switch>
</BrowserRouter>
)
}
}
const Portfolio = props => <FooContext.Consumer>{foo => <SubmitProject/>}</FooContext.Consumer>;
export default App;
SubmitProject.js
import React from 'react';
import PortfolioForm from './PortfolioForm';
import Section from './Section';
class SubmitProject extends React.Component {
render() {
return(
<React.Fragment>
<h1>Submit Project</h1>
<h2>Enter Project Data</h2>
<ul className="section">
{Object.keys(this.state.sections).map(key => <Section key={key} details={this.state.sections[key]}/>)}
</ul>
<PortfolioForm addSection={this.addSection} />
</React.Fragment>
)
}
}
export default SubmitProject;
It has been recommend to use a state manager like context api, or something else, but I want to know if there is a way to restructure my App and be able to pass state from a top level component that each component and route share.
There are problems with this approach.
Considering that App maintains application state, it's necessary to pass it to <Router> as a prop and then to route components that depend on it:
class App extends React.Component {
state = { foo: true };
render() {
return <Router foo={this.state.foo}/>
}
}
const Router = props => (
const RootWithFoo = props => <Root foo={props.foo}/>;
return <BrowserRouter>
<Switch>
<Route exact path="/" component={RootWithFoo} />
...
</Switch>
</BrowserRouter>
);
This puts a restriction on component structure; in order to avoid deeply passed props, Router component should be removed, and Route should be rendered directly in App:
class App extends React.Component {
state = { foo: true };
render() {
const RootWithFoo = props => <Root foo={this.state.foo}/>;
return <BrowserRouter>
<Switch>
<Route exact path="/" component={RootWithFoo} />
...
</Switch>
</BrowserRouter>
}
}
This is a problem that context API and state management libraries (e.g. Redux) address. They allow to provide global state for nested components where it's used.
Another problem is performance. The whole router will be re-rendered on each state update. Again, context API and state management libraries address this. As context API manual states:
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.
So if context provider value updates, it's unnecessary to re-render the whole tree. Context consumer will be re-rendered regardless of this. Since the whole tree will be re-rendered by default, Provider child(ren) should be a pure component to avoid unnecessary re-renders. This is a reason why separated App and Router components may be preferable:
class App extends React.Component {
state = { foo: true };
render() {
return <FooContext.Provider value={this.state.foo}>
<Router/>;
</FooContext.Provider>
}
}
class Router extends React.PureComponent {
render() {
return <BrowserRouter>
<Switch>
<Route exact path="/" component={Root} />
...
</Switch>
</BrowserRouter>
}
}
const Root = props => <FooContext.Consumer>{foo => ...}</FooContext.Consumer>;
When global state is updated, only App and route components that depend on it (Root, etc.) are re-rendered but not Router.

React Router (Dom) v4 redirect to different route upon input enter key press

I am trying to redirect to new route when user presses enter on input field.
I have a Title and Search component that I want rendered on every page.
I have found different use cases with using Redirect component, withRouter component, using context, and possibly passing history object to my Search component which is where the input field lives. Any help would be appreciated..
App.js (main component)
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Title from './Title';
import Search from './Search';
import Home from './Home';
import Movie from './Movie';
class App extends Component {
render() {
return (
<div>
<Title />
<Search />
<Router>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/movie' component={Movie} />
</Switch>
</Router>
</div>
);
}
}
export default App;
Search.js (input component)
import React, { Component } from 'react';
import './Search.css';
class Search extends Component {
constructor(props) {
super(props);
this.state = {
value: ""
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(e) {
if (e.key === 'Enter') {
// TODO redirect user to '/movie'
}
}
render() {
return (
<input type="text" id="searchTitle" placeholder="Search for a movie" onChange={this.handleChange} onKeyPress={this.handleSubmit} value={this.state.input} />
)
}
}
export default Search;
inside your handleSubmit function try:
this.props.history.push('/movie'); // or whatever
edit:
you'll probably need to bind this as well
onKeyPress={this.handleSubmit.bind(this)}
and do this to the component you're passing into the route
const HomePage = () => {
return <Home props={this.props} />
}
...
<Route ... component={HomePage} />
All other solutions I came across were outdated because of the new version.
Refer to React Router V4 / Redirect.
I have finally solved the problem by doing the following;
import { Redirect } from 'react-router-dom'
And then in my state I have declared 'submitted' as false.
postForm(e){
e.preventDefault();
this.setState({
submitted : true
})
}
When you are returning inside your render method it is going to check with your state and
render(){
if (this.state.submitted) {
return (
<Redirect to="/done"/>
)
}
return (
something else...
)
I don't know if this is one of the good solutions but unfortunately I couldn't get it to work in any other way.
You can see here to get more information in your case https://gist.github.com/verticalgrain/195468e69f2ac88f3d9573d285b09764

Categories