简体   繁体   中英

How do I transmit a state to a parent component when clicking a react-router Link?

I want to build a single page application that works with transitions, that would look like follows:

*------*--------------*-----------*
|      |              |           |
| Shop | Landing Page | Biography |
|      |              |           |
*------*--------------*-----------*
|                                 |
|          Contact page           |
|                                 |
*---------------------------------*

Basically, when you first come on the website, you arrive on the landing screen. From there, 3 links are available, 'Biography', 'Shop' and 'Contact' ('Contact' is available from the three 'top' pages).

Once you click on the link to go to either 'Shop' or 'Biography', you can go back to the landing or go to the contact page, but you can't go the the "opposite" page (hence the schema). Also, when you get to contact page and click 'back', you'll get to the last page you were before.

I created a CodeSandBox to avoid pasting half a kilometer of code that don't necessarly have anything to do with this but are still a bit concerned

So I would like to be able to precisely setup my transitions, like going from left to right when going to 'Biography' (and the opposite when leaving), right to left when going to "Shop", and bottom to top when going to the contact form.

After reading tons of issues and documentations about react-transition-group , I had the idea of doing something like this:

// src/App.js
const PageFade = props => (
  // props.transition would contain "topBottom", "bottomTop", "leftRight" and "rightLeft" 
  // (so that I would ajust my CSS in consequence)
  {props.transition === 'topBottom' &&
  <CSSTransition
    {...props}
    classNames={props.transition !== null && props.transition}
    timeout={1000}
    mountOnEnter={true}
    unmountOnExit={true}
  />
  }
);
}

I could do this or create a <CSSTransition /> for each possibility, I don't really know.

My problem is that I need to tell this <CSSTransition /> either what have been clicked or (better) what transition it needs to display.

To do so, I tried to set a transition state, but it looks like it gets too much refreshed, and my knowledge of React is rusty, and I don't know how to handle all this with react-route.

Update

Thank you all for your answers, and sorry for not answering faster, my computer is freezing beyond madness.

So I created a new class CSSTransitions where I copied / pasted @brandon's answer while adapting it to my needs. However, I have a problem with the componentWillReceiveProps(nextProps) , because it nextProps.location is null, somehow, the react-router's context isn't passed.

I updated my CodeSandBox, the CSSTransitions class is in an 'utils' directory inside the 'src' directory.

Thank you again for your answers

Thank you in advance

My suggestion is to just have your PageFade component monitor the routes and compare previous route to new route and change its direction based on the change in routes. Here's a rough example:

import {withRouter} from 'react-router';

const directions = {
  "/->/contact": "top-bottom",
  "/contact->/": "bottom-top",
  // etc...
}

class PageFadeDumb extends React.Component {
  state = {direction: "top-bottom"};

  componentWillReceiveProps(nextProps) {
    if (nextProps.location.pathname !== this.props.location.pathname) {
      const transitionPath = `${this.props.location.pathname}->${nextProps.location.pathname}`;
      const newDirection = directions[transitionPath] || "top-bottom";
      console.log(`newDirection = ${newDirection}`);
      this.setState({direction: newDirection});
    }
  }

  render() {
    const direction = this.state.direction;
    // use here somehow?
    return (
      <CSSTransition
        {...(this.props)}
        classNames="fadeTranslate"
        timeout={1000}
        mountOnEnter={true}
        unmountOnExit={true}
      />
    );
  }
}

const PageFade = withRouter(PageFadeDumb);

I would do it this way:

class ParentComponent extends React.Component {

  state = {
    some_state: ""
  }

  myCallback = (ev) => {
    this.setState({ some_state: ev.target.dataset.mydata });
  }

  render() {
    <ChildComponent cback={this.myCallback} />  // Insert all other props too
  }

}

class ChildComponent extends React.Component {

  render() {
    <button data-mydata="Value you need to pass up" onClick={this.props.cback}>
      Click me
    </button>
  }

}

When the button is clicked the callback defined in ParentComponent is triggered and receives an Event object and then you may capture one or more values using data- attributes .

To learn more about these data- attributes and HTMLElement.dataset you may read this .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM