I have set the state in a component as follows:
class App extends Component {
constructor(props) {
super(props)
this.state = {
minutes: 3,
interests: {
business: false,
code: false,
design: false
}
}
}
render() {
return (
<div className="App">
<div className="content">
<div className="centered-wrapper">
<Switch>
<Route exact path="/" component={Welcome} />
<Route path="/life" component={Life} />
<Route path="/work" component={Work} />
<Route path="*" component={Welcome}/>
</Switch>
</div>
</div>
);
}
}
export default App;
And I'm trying to use the state in one of the components managed by the router in the following way:
export class Welcome extends Component {
constructor(props) {
super(props);
this.state = {
errors: []
};
}
render() {
return (
<form className="Welcome">
<input className="minutes" type="number" defaultValue={ this.state.minutes } /> minutes.
</form>
);
}
}
But it doesn't work. Is state not global by default?
State is only available inside the component which defines it. If you want to use state in another component, you have to pass it using props.
To pass props along with your Route, you can use the render function.
You can then call your component using:
<Route exact path="/" render={props => <Welcome {...props} minutes={this.state.minutes} />} />
And then in the Welcome components use the passed minutes prop instead of state.
Related
I'm new to react and redux world. I have a question regarding react router. Following is my App component.
class App extends React.PureComponent {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/callback" component={CallbackPage} />
<Route exact path="/list" component={List} />
<Route component={NotFoundPage} />
</Switch>
</div>
);
}
}
export default App;
My list class looks like this:
export class List extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isOpen: false,
};
this.depositHandleToggle = this.depositHandleToggle.bind(this);
}
depositHandleToggle = () => {
this.props.dispatch(push(`${this.props.match.url}/deposit`));
}
render() {
const { match } = this.props;
return (
<div>
<Route path={`${match.url}/deposit`} component={Modal} />
<button onClick={this.depositHandleToggle}>Open Modal</button>
</div>
);
}
}
export default List;
So my question is: when i click the button in List container, why my router can't find this url 'localhost:xxx/list/deposit'? it renders the App component but it never goes back to List component. Is it possible to have the custom routes inside my list container? Am i doing something wrong? Please someone help me with this.
I hope you understand my question. Thanks in advance.
ANSWER:
I found the answer, the issue was in my App component list route. I was having the 'exact' keyword, that's why route inside my list component was not working. Following way is the correct way.
<Route path="/list" component={List} />
I hope this will help someone.
import { Link } from 'react-router-dom';
class App extends React.PureComponent {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/callback" component={CallbackPage} />
<Route exact path="/list" component={List} />
<Route exact path="/list/deposit" component={Modal} />
<Route component={NotFoundPage} />
</Switch>
</div>
);
}
}
export default App;
export class List extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
const { match } = this.props;
return (
<div>
<Link to={'/deposit'}>Open Modal</Link>
);
}
}
export default List;
in that component you should handle modal open close.
The app.js file of my project is following. For all other components I used this.props.history.push('/') to redirect to the specific path and it works as those components receives the props. But in this app.js I have tried to console the props values inside constructor, componentWillMount and render but all gives null array. Is there any way to use this.props.history.push('/') inside app.js ?
class App extends Component {
constructor(props) {
super(props);
this.state = {};
console.log(this.props)
}
componentWillMount(){
console.log(this.props)
this.props.history.push('/')
}
render() {
console.log(this.props)
return (
<Router>
<div className="App">
<Route exact path='/' render={(props) => <Login {...props} />} />
<Route exact path='/dashboard' render={(props) => <Dashboard {...props}/>}/>
</div>
</Router>
);
}
}
export default App;
use withRouter
import { withRouter } from 'react-router-dom'
class App extends Component {
constructor(props) {
super(props);
this.state = {};
console.log(this.props)
}
componentWillMount(){
console.log(this.props)
this.props.history.push('/')
}
render() {
console.log(this.props)
return (
<Router>
<div className="App">
<Route exact path='/' render={(props) => <Login {...props} />} />
<Route exact path='/dashboard' render={(props) => <Dashboard {...props}/>}/>
</div>
</Router>
);
}
}
export default withRouter(App);
that will give you access to the history object and allow you to push to new routes. (I tested and verified before posting, so I know this works).
import it on the top of the file, then wrap App with it in the export default
I am rendering a Home component inside a Route so that I can pass state in as a prop to the component.
class App extends React.Component {
constructor(props){
super(props)
this.state = {
page: 'false'
};
}
render() {
return (
<Router>
<div>
<Switch>
<Route exact path='/' render={()=><Home page={this.state.page}/>} />
<Route exact path='/projects' component={Projects} />
<Route render={function(){
return <p>Not Found</p>
}} />
</Switch>
</div>
</Router>
)
}
}
Inside the Home component, I want to trigger a route change from a function. Because the Component is rendered inside the Route the history prop doesn't get passed in and therefore I cannot trigger a route change like so:
class Home extends React.Component{
constructor(props){
super(props);
this.gotoProjects = this.gotoProjects.bind(this);
}
gotoProjects() {
this.props.history.push('/projects');
}
render() {
return (
<button onClick={this.gotoProjects.bind(this)}>Projects</button>
)
}
}
How can I change routes from a component while still retaining it's props?
UPDATE
I've created a History.js using createBrowserHistory
import { createBrowserHistory } from 'history'
export default createBrowserHistory()
And updated App.js to be
import history from '../history';
class App extends React.Component {
constructor(props){
super(props)
this.state = {
page: 'false'
};
}
render() {
return (
<Router>
<div>
<Switch>
<Route exact path='/' render={()=><Home history={history} page={this.state.page}/>} />
<Route exact path='/projects' component={Projects} />
<Route render={function(){
return <p>Not Found</p>
}} />
</Switch>
</div>
</Router>
)
}
}
So now when I click the button in Home the url goes to /projects but the view is still rendering Home instead of Projects. How do I render Projects after the history.push happens?
I have react router setup with route parameters :name
<Router>
<Switch>
<Route path="/" exact={true} component={Index} />
<Route path="/about/:name" component={About} />
<Route component={NotFound} />
</Switch>
</Router>
Using <Link to=, links from Index correctly route to eg /about/vinnie.
However, if the <Link to= component is on the About page, clicking the link merely updates the browser URL, but dies not re-render the correct page.
Any clues why this might be happening?
About.jsx
render () {
const id = this.props.match.params.name;
return (
<div className="Explore">
<div className="container">
<Api endpoint={[this._apiEndpoint(id, 'info')]} loadData={true}>
<CustomerInfo />
</Api>
...
Api.jsx
render () {
let showData = this.props.loadData ? null : <button onClick={() => this._loadButton()}>Show</button>;
return (
<span>
{this.props.children && React.cloneElement(this.props.children, {
apiData: this.state.rawData
})}
{showData}
</span>
);
};
CustomerInfo.jsx
class CustomerInfo extends React.Component {
constructor(props) {
super(props);
this.state = {
CustomerInfo: {}
}
}
componentWillReceiveProps(nextProps) {
if (this.props.apiData !== nextProps.apiData) {
this.setState({CustomerInfo: nextProps.apiData[0]});
}
}
render() {
...
I think you need to add the exact prop to your first <Route />. Otherwise your routes match Index and About and I think react-router intentionally renders both.
I have a router config like this:
<Router history={browserHistory}>
<Route path="/">
<IndexRoute component={HomePageContainers} />
<Route path="r/:subreddit_name" component={Subreddit} />
</Route>
</Router>
HomePageContainers renders like:
render() {
return (<div>
<HomePage reddits={this.state.reddits} key={this.state.isLoaded && this.state.reddits.data.after} isLoaded={this.state.isLoaded} isScrolling={this.state.isScrolling} scrollFunc={this.onScroll.bind(this)} />
</div>)
}
and HomePage render Reddits like:
<Col md={3} key={reddit.data.id}>
<Reddits key={reddit.data.id} reddit={reddit}/><br/>
</Col>
There is a Reddits class which looks like:
class Reddits extends React.Component {
constructor(props){
super(props);
}
render() {
let reddit = this.props.reddit;
return(
<div id={styles.box}>
<p>{reddit.data.title}</p>
<Link to={reddit.data.url}>
<p>{reddit.data.url}</p>
</Link>
</div>
)
}
}
whenever the path is r/:subreddit_name made, Subreddit component gets life and its working. Here I also get the :subreddit_name value as part of routeParams. But if you look at the Reddits class, the reddit.data has all the information for Subreddit.
I want to send the reddit.data as props from Route(r), is it possible?
You will want to have HomeComponenet render your Reddit component as children like so:
<Route path='/home' component="HomeComponent">
<Route path='/reddit' component="RedditComponent">
</Route>
To get reddit component to display you need to render children in your HomeComp:
render() {
{ this.props.children }
}
Now you can pass in props via cloning children like so:
HomeComp:
render() {
{this.props.children && React.cloneElement(this.props.children, {
someFunctionOrDataToPass: this.someFunctionOrDataToPass
})}
}
Here is another example