Skip to content

Commit

Permalink
Fix prop issue (#8)
Browse files Browse the repository at this point in the history
* Fix child properties issue

In the current release, the props given to the components under the
TransitionSwitch are not dynamic anymore. This is due to the fact that
we store a clone from this.props.children in the internal state of
TransitionSwitch.

The current solution is to only store the key in the state, and in the
render method we retrive in this.props.children the ones that match the
keys from the state, and we render these

* Add support for render and component props of <Route />

Add support for <Route/>'s component prop. It is the most common use
case and it is now supported.

Add support for <Route/>'s render prop. It is now going to fully support
the react router v4 Route API.

TransitionSwitch no longer returns the underlying Route, but only what
the Route element would render, meaning the child given to the Route
via component, render, or children.
  • Loading branch information
aboeglin committed Jul 17, 2017
1 parent b398ccb commit 4bece6a
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 42 deletions.
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,3 @@ npm run start:server
The app will be running at localhost:8080, the build command watches for changes in case you want to play with it, the
sources are located in src/example.



In the next update:
- maybe emit transition state changes to the parent of TransitionSwitch via props ? In order to sync transition with
other parts.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"author": "Arnaud Boeglin",
"license": "MIT",
"name": "react-router-v4-transition",
"version": "0.1.5",
"version": "0.1.7",
"description": "",
"main": "lib/react-router-v4-transition.js",
"repository": {
Expand Down
107 changes: 74 additions & 33 deletions src/TransitionSwitch.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class TransitionSwitch extends React.Component {
children: PropTypes.oneOfType([
PropTypes.arrayOf(routePropType),
routePropType
]),//PropTypes.node,
]),
location: PropTypes.object
};

Expand All @@ -43,8 +43,9 @@ export class TransitionSwitch extends React.Component {
this.leavingRouteChildRef = null;

this.state = {
enteringRoute: null,
leavingRoute: null
enteringRouteKey: null,
leavingRouteKey: null,
match: null
}
}

Expand Down Expand Up @@ -84,64 +85,103 @@ export class TransitionSwitch extends React.Component {
found = true;

//In case it's the current child we do nothing
if(this.state.enteringRoute) {
if(this.state.enteringRoute.key == child.key)
if(this.state.enteringRouteKey) {
if(this.state.enteringRouteKey == child.key)
return
}

//If it's not parallel, it would happen when a route change occurs while transitioning.
//In that case we keep the original leaving element, and we just replace the entering element
if(!this.state.leavingRoute || this.props.parallel) {
if(!this.state.leavingRouteKey || this.props.parallel) {
this.leavingRouteChildRef = this.enteringRouteChildRef;
this.enteringRouteChildRef = null;
}

let enteringRoute = React.cloneElement(child, {
children: React.cloneElement(child.props.children, {
ref: ref => {
if(ref)
this.enteringRouteChildRef = ref
}
})
});

this.setState({
...this.state,
leavingRoute: this.state.leavingRoute && !this.props.parallel ? this.state.leavingRoute : this.state.enteringRoute,
enteringRoute: enteringRoute
leavingRouteKey: this.state.leavingRouteKey && !this.props.parallel ? this.state.leavingRouteKey : this.state.enteringRouteKey,
enteringRouteKey: child.key,
match: match
});
}
});

//In case we didn't find a match, the enteringChild will leave:
if(!found && this.state.enteringRoute) {
if(!found && this.state.enteringRouteKey) {
this.leavingRouteChildRef = this.enteringRouteChildRef;
this.enteringRouteChildRef = null;

this.setState({
...this.state,
leavingRoute: this.state.enteringRoute,
enteringRoute: null
leavingRouteKey: this.state.enteringRouteKey,
enteringRouteKey: null,
match: null
});
}
}

render() {
let enteringRoute = null;
let enteringChild = null;
let leavingChild = null;

const props = {
match: this.state.match,
location: this.getLocation(this.props, this.context),
history: this.context.router.history,
staticContext: this.context.router.staticContext
};

React.Children.map(this.props.children, child => child).forEach(child => {

if(child.key == this.state.enteringRouteKey) {
let component = null;

if(child.props.component)
component = React.createElement(child.props.component);
else if(child.props.render)
component = child.props.render(props);
else
component = child.props.children;

enteringChild = React.cloneElement(component, {
ref: ref => {
if (ref)
this.enteringRouteChildRef = ref
},
key: `child-${child.key}`
});
}
else if(child.key == this.state.leavingRouteKey) {
let component = null;

if(child.props.component)
component = React.createElement(child.props.component);
else if(child.props.render)
component = child.props.render(props);
else
component = child.props.children;

leavingChild = React.cloneElement(component, {
ref: ref => {
if (ref)
this.leavingRouteChildRef = ref
},
key: `child-${child.key}`
});
}

//If it's not parallel, we only render the enteringRoute when the leavingRoute did leave
});

// If it's not parallel, we only render the enteringRoute when the leavingRoute did leave
if(!this.props.parallel) {
if(!this.state.leavingRoute)
enteringRoute = this.state.enteringRoute;
}
else {
enteringRoute = this.state.enteringRoute;
if(this.state.leavingRouteKey)
enteringChild = null
}

return (
<div>
{enteringRoute}
{this.state.leavingRoute}
{enteringChild}
{leavingChild}
</div>
);
}
Expand All @@ -165,17 +205,18 @@ export class TransitionSwitch extends React.Component {
if(prevLocation.pathname == location.pathname && prevMatch.isExact == match.isExact)
return;

if(this.state.enteringRoute && this.enteringRouteChildRef && this.enteringRouteChildRef.componentWillEnter) {
if(this.props.parallel)
if(this.state.enteringRouteKey && this.enteringRouteChildRef && this.enteringRouteChildRef.componentWillEnter) {
if(this.props.parallel) {
this.enteringRouteChildRef.componentWillEnter(() => this.enteringChildEntered());
}
}
else {
this.enteringChildEntered();
}

//If there's a ref and there wasn't a leaving route in the previous state
if(this.leavingRouteChildRef && this.leavingRouteChildRef.componentWillLeave) {
if(this.leavingRouteChildRef && (!prevState.leavingRoute || this.props.parallel)) {
if(this.leavingRouteChildRef && (!prevState.leavingRouteKey || this.props.parallel)) {
this.leavingRouteChildRef.componentWillLeave(() => this.leavingChildLeaved());
}
}
Expand Down Expand Up @@ -210,7 +251,7 @@ export class TransitionSwitch extends React.Component {
this.leavingRouteChildRef = null;
this.setState({
...this.state,
leavingRoute: null
leavingRouteKey: null
});

//If it's not parallel, we start the entering transition when the leaving child has left.
Expand Down
6 changes: 3 additions & 3 deletions src/TransitionSwitch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ describe('TransitionSwitch', () => {
routerWrapper.node.history.push('/'); //We go to "/"
routerWrapper.node.history.push('/404'); //We go to a non existing route

expect(wrapper.find(TransitionSwitch).node.state.enteringRoute).toBe(null);
expect(wrapper.find(TransitionSwitch).node.state.leavingRoute).not.toBe(null);
expect(wrapper.find(TransitionSwitch).node.state.enteringRouteKey).toBe(null);
expect(wrapper.find(TransitionSwitch).node.state.leavingRouteKey).not.toBe(null);

jest.runAllTimers(); //We run the leaving transition
expect(wrapper.find(TransitionSwitch).node.state.leavingRoute).toBe(null);
expect(wrapper.find(TransitionSwitch).node.state.leavingRouteKey).toBe(null);
});

it('should do nothing if there is no route change', () => {
Expand Down
45 changes: 45 additions & 0 deletions src/example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class ExampleApp extends React.Component {
<div className="example-app">
<nav className="example-app__menu">
<Link to="/">Home</Link>
<Link to="/aTransition">A Transition</Link>
<Link to="/useRender">Render</Link>
<Link to="/otherPath">Other Path</Link>
<Link to="/anotherPath">Another Path</Link>
</nav>
Expand All @@ -28,6 +30,12 @@ class ExampleApp extends React.Component {
<Route exact path="/">
<Transition>home path</Transition>
</Route>
<Route path="/aTransition" component={ATransition}/>
<Route path="/useRender" render={(props) => {
return (
<Transition>use render</Transition>
);
}}/>
<Route path="/otherPath">
<Transition>other path</Transition>
</Route>
Expand Down Expand Up @@ -84,6 +92,43 @@ class Transition extends React.Component {

}

class ATransition extends React.Component {
constructor(props) {
super(props);
}

componentWillAppear(cb) {
TweenLite.fromTo(ReactDOM.findDOMNode(this), d, {x: -100, opacity: 0}, {x: 0, opacity:1, onComplete: () => cb()});
}

// componentDidAppear() {
// //do stuff on appear
// }

componentWillEnter(cb) {
TweenLite.fromTo(ReactDOM.findDOMNode(this), d, {x: 100, opacity: 0}, {x: 0, opacity:1, onComplete: () => cb()});
}

componentDidEnter() {
//do stuff on enter
}

componentWillLeave(cb) {
// if(this.mounted)
TweenLite.to(ReactDOM.findDOMNode(this), d, {x: -100, opacity:0, onComplete: () => cb()});
}

componentDidLeave() {
//do stuff on leave
}

render() {
return (
<div className="example-app__transition">A Transition</div>
);
}
}

ReactDOM.render(
<BrowserRouter>
<ExampleApp />
Expand Down

0 comments on commit 4bece6a

Please sign in to comment.