-
Notifications
You must be signed in to change notification settings - Fork 978
Button & React Router #984
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
What I am doing is enveloping the button with the react router's link: import React from 'react';
import RtButton from 'react-toolbox/lib/button/Button';
import {Link} from 'react-router';
export class Button extends React.Component {
render() {
const {href, ...otherProps} = this.props;
if (href == undefined) {
return <RtButton {...otherProps}/>;
}
return (
<Link to={href}>
<RtButton {...otherProps}/>
</Link>
);
}
};
export default Button; Also you may want to change the import to: import RtButton from 'react-toolbox/lib/button'; If you are not loading the themes manually like I do. |
As I had some troubles with the previous implementation, I created a package to interface with the v4 router. |
What about an HOC? Try this: const withReactRouterLink = Component =>
class Decorated extends React.Component {
static propTypes = {
activeClassName: PropTypes.string,
className: PropTypes.string,
to: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func
])
}
static contextTypes = {
router: PropTypes.object
};
resolveToLocation = to => {
const { router } = this.context;
return typeof to === 'function' ? to(router.location) : to
}
handleClick = event => {
const { to } = this.props;
const { router } = this.context;
event.preventDefault();
router.push(this.resolveToLocation(to));
}
render () {
const { router } = this.context;
const { activeClassName, className, to, ...rest } = this.props;
const toLocation = this.resolveToLocation(to);
const isActive = router.isActive(toLocation);
const _className = isActive ? `${className} ${activeClassName}` : className;
return (
<Component
{...rest}
className={_className}
href={toLocation}
onClick={this.handleClick}
/>
);
}
}; Then you can create a wrapper around React Toolbox and use it like a import { Button } from 'react-toolbox/lib/button';
const ReactToolboxLink = withReactRouterLink(Button);
export const Test = () =>
<ReactToolboxLink activeClassName="active" to="/location" primary raised /> I don't know if it works with RR4 though. |
I would actually love to have a component that supports it as part of this library, it could have a property to enable support. The main issue are themes, the only way to have it fully working "out of the box" was to clone the current code for the button, iconbutton, link and make minor changes so that it works as before but now it supports router's link and I don't have to know the internals (it just forwards the props). In theory that could work with router v1, v2, v3 and v4 because if I am not mistaken, the required props for the link component didn't change since the first version, but I only tested with the v4. |
I agree with @borela. No way to setup the typical routing behavior for Single Page Applications is a major drawback. |
Have modified the HOC for react router 4 RRHoc.js import React, {Component, PropTypes } from 'react'
import { withRouter } from 'react-router-dom'
export const withReactRouterLink = Component => {
class Decorated extends React.Component {
constructor(props,context) {
super(props,context)
this.state = {active: false}
}
static propTypes = {
activeClassName: PropTypes.string,
className: PropTypes.string,
target: PropTypes.string,
to: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]).isRequired,
};
resolveToLocation = to => {
return typeof to === 'object' ? to['pathname'] : to
}
isActive = (toLocation, nextProps) => {
const currProps = nextProps || this.props
const { location, to } = currProps
return toLocation == location.pathname
}
handleClick = event => {
event.preventDefault();
const { to } = this.props;
this.props.push(to)
this.setState({active: this.isActive(to)})
}
componentWillMount() {
const { to } = this.props;
this.setState({active: this.isActive(to)})
}
componentWillReceiveProps(nextProps) {
const { to } = this.props;
if (this.state.active != this.isActive(to,nextProps)) {
this.setState({active: this.isActive(to,nextProps)})
}
}
shouldComponentUpdate(nextProps, nextState) {
const { to } = this.props;
return this.state.active != nextState.active
}
render () {
const { activeClassName, className, to, ...rest } = this.props;
const toLocation = this.resolveToLocation(to);
const _className = this.state.active ? `${className} ${activeClassName}` : className;
return (
<Component
{...rest}
className={_className}
href={toLocation}
onClick={this.handleClick}
/>
);
}
};
return withRouter(Decorated)
} Usage import { Route, BrowserRouter as Router } from 'react-router-dom'
import { List, ListItem } from 'react-toolbox'
import React, { Component, PropTypes } from 'react';
import {withReactRouterLink} from './RRHoc.js'
const RRListItem = withReactRouterLink(ListItem)
const RrRt = () => (
<List>
<RRListItem caption="Home" to="/home" activeClassName="customRouterListActive" className="customRouterList" />
<RRListItem caption="School" to="/School" activeClassName="customRouterListActive" className="customRouterList" />
<RRListItem caption="Work" to="/Work" activeClassName="customRouterListActive" className="customRouterList" />
</List>
)
const Expose = () => (
<Router>
<div>
<RrRt/>
<Route path="/home" component={()=> <h1> Home </h1>} />
<Route path="/School" component={()=> <h1> School </h1> } />
<Route path="/Work" component={()=> <h1> Work </h1> } />
</div>
</Router>
)
export default Expose css .customRouterList {
text-decoration: none;
}
.customRouterListActive {
background-color : rgb(238, 238, 238);
} |
@javivelasco I know this has been an old issue, but just want your thoughts. I've been experimenting with passing Functions as children vs HOC for this particular use case, so we can passing any Element as a child to this component. RRLinkFunc.js import React, {Component, PropTypes } from 'react'
import { withRouter } from 'react-router-dom'
class Decorated extends React.Component {
constructor(props,context) {
super(props,context)
this.state = {active: false}
}
static propTypes = {
to: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]).isRequired,
children: React.PropTypes.func.isRequired,
};
resolveToLocation = to => {
return typeof to === 'object' ? to['pathname'] : to
}
isActive = (toLocation, nextProps) => {
const currProps = nextProps || this.props
const { location, to } = currProps
return toLocation == location.pathname
}
handleClick = event => {
event.preventDefault();
const { to } = this.props;
this.props.push(to)
this.setState({active: this.isActive(to)})
}
componentWillMount() {
const { to } = this.props;
this.setState({active: this.isActive(to)})
}
componentWillReceiveProps(nextProps) {
const { to } = this.props;
if (this.state.active != this.isActive(to,nextProps)) {
this.setState({active: this.isActive(to,nextProps)})
}
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.active != nextState.active
}
render () {
const { to } = this.props;
const toLocation = this.resolveToLocation(to);
return this.props.children(toLocation,this.handleClick,this.state.active)
}
}
export default withRouter(Decorated) Usage import { Route, BrowserRouter as Router } from 'react-router-dom'
import { List, ListItem } from 'react-toolbox'
import {Button} from 'react-toolbox/lib/button';
import React, { Component, PropTypes } from 'react';
import RRLink from './RRLinkFunc.js'
const RrRt = () => (
<div>
<List>
<RRLink key={1} to="/home">
{
(location,handler,isActive) => {
const className = 'customRouterList'
const activeClassName = 'customRouterListActive'
const _className = isActive ? `${className} ${activeClassName}` : className;
return <ListItem caption="Home" onClick={handler} className={_className} />
}
}
</RRLink>
<RRLink key={2} to="/school">
{
(location,handler,isActive) => {
const className = 'customRouterList'
const activeClassName = 'customRouterListActive'
const _className = isActive ? `${className} ${activeClassName}` : className;
return <ListItem to={location} caption="School" onClick={handler} className={_className} />
}
}
</RRLink>
</List>
<RRLink key={3} to="/work">
{
(location,handler,isActive) => {
const className = 'customRouterList'
const activeClassName = 'customRouterListActive'
const _className = isActive ? `${className} ${activeClassName}` : className;
return <Button href={location} label="Work" onClick={handler} />
}
}
</RRLink>
</div>
)
const Expose = () => (
<Router>
<div>
<RrRt/>
<Route path="/home" component={()=> <h1> Home </h1>} />
<Route path="/school" component={()=> <h1> School </h1> } />
<Route path="/work" component={()=> <h1> Work </h1> } />
</div>
</Router>
)
export default Expose |
Neither of the proposed solutions up there works for me with RRv4.2. But either of those examples is overly complicated and can be simplified. If you take a look at the example for the custom link provided at react router documentation: https://reacttraining.com/react-router/web/example/custom-link You can just alter a code and make it work with React Toolbox import React from 'react'
import PropTypes from 'prop-types'
import { Route } from 'react-router-dom'
import Link from 'react-toolbox/lib/link'
const handleClick = (event, history, to) => {
event.preventDefault()
history.push(typeof to === 'object' ? to.pathname : to)
}
const RRLink = ({ to, exact, strict, ...rest }) =>
<Route path={to} exact={exact} strict={strict} children={({ history, match }) => (
<Link {...rest} active={!!match} onClick={(event) => handleClick(event, history, to)} />
)} />
RRLink.propTypes = {
to: PropTypes.oneOfType([
PropTypes.shape({
pathname: PropTypes.string.isRequired
}),
PropTypes.string
]).isRequired,
exact: PropTypes.bool,
strict: PropTypes.bool
}
export default RRLink import RRLink from './RRLink'
...
<RRLink exact to='/' label='Home' icon='home' />
<RRLink strict to='/locations' label='Lokacije' icon='place' />
... This code simulates react router's |
Another solution for Button component:
Usage:
|
I've seen the Button component has a href prop which you can pass a URI.
Can it interface with React Router as the Link component?
The text was updated successfully, but these errors were encountered: