1

I've built a modal in ReactJS which needs to be triggered by clicking an <a> to add .active class to the modal <div className="newsletterModal">.

Once class is active as newsletterModal active the onClick={this.toggle.bind(this)} is successful in removing the active class but how can I add the active class from within my footer?

In Newsletter.js

import React from 'react'
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
class Newsletter extends React.Component {
    constructor(props) {
      super(props);
      this.state = {addClass: false}
    }
    toggle() {
      this.setState({addClass: !this.state.addClass});
    }
    render() {
      let toggleModal = ["newsletterModal"];
      if(this.state.addClass) {
        toggleModal.push('active');
      }
      return(
            <div className={toggleModal.join(' ')}>
                <div className="newsletterContainer">
                   <span className="modalClose">
                       <a onClick={this.toggle.bind(this)} href="javascript:;"><svg>...</svg></a>
                   </span>                       
                   <content /> 
                </div>
            </div>
        );
    }
}
export default Newsletter

In Footer.js (where I want the link to add the class set within Newsletter.js)

import React from 'react'
import PropTypes from 'prop-types';
import Link from 'gatsby-link'
const Footer = (props) => (
    <footer>
        <a onClick={this.toggle.bind(this)} href="javascript:;">
            Newsletter
        </a>        
    </footer>
)
export default Footer

index.js where both are called to the template - please note that I have a class being added to show the menu too from within this file. Perhaps it is possible to combine my is-menu-visible with newsletterModal active?

import React from 'react'
import PropTypes from 'prop-types';
import Helmet from 'react-helmet'
import { Link, withPrefix } from 'gatsby-link'
import '../assets/scss/main.scss'
import Header from '../components/global/Header'
import Menu from '../components/global/Menu'
import Newsletter from '../components/global/Newsletter'
import Footer from '../components/global/Footer'
class Template extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            isMenuVisible: false,
            loading: 'is-loading'
        }
        this.handleToggleMenu = this.handleToggleMenu.bind(this)
    }

    componentDidMount () {
        this.timeoutId = setTimeout(() => {
            this.setState({loading: ''});
        }, 100);
    }

    componentWillUnmount () {
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
        }
    }

    handleToggleMenu() {
        this.setState({
            isMenuVisible: !this.state.isMenuVisible
        })
    }

    render() {
        const { children } = this.props

        return (
            <main id="app" className={`body ${this.state.loading} ${this.state.isMenuVisible ? 'is-menu-visible' : ''}`}>
                <Helmet>
                    <link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
                </Helmet>
                <div id="wrapper">
                    <div className="sub-wrapper">
                        <Header onToggleMenu={this.handleToggleMenu} />
                        <div className="gutter">
                            {children()}
                            <Footer />
                        </div>
                    </div>
                </div>
                <Menu/>
                <Newsletter />
            </main>
        )
    }
}

Template.propTypes = {
    children: PropTypes.func
}

export default Template

2 Answers 2

2

It can be done by adding showActive state in Template class.

Add callback function in Footer:

const Footer = props => (
  <footer>
    <a onClick={props.onClick}>Newsletter</a>
  </footer>
);

Instead of setState inside, read addClass from props:

class Newsletter extends Component {
  render() {
    let toggleModal = ["newsletterModal"];
    if (this.props.addClass) {
      toggleModal.push("active");
    }
    return (
      <div className={toggleModal.join(" ")}>This part should update!</div>
    );
  }
}

Add an event handler for footer onClick event:

const Header = props => (
  <header onClick={props.onToggleMenu}>
    <h1>Main Page Title</h1>
  </header>
)

class Template extends Component {
  constructor(props) {
    super(props);
    this.state = {
      showActive: false
    };
  }

  toggleClass = () => {
    this.setState(prevState => ({
      showActive: !prevState.showActive
    }));
  };

  render() {
    return (
      <main>
        <Header onToggleMenu={this.toggleClass} />
        <Newsletter addClass={this.state.showActive} />
        <Footer onClick={this.toggleClass} />
      </main>
    );
  }
}

Note:
If you have multiple components interact with each other, it's better to consider using a state manager, such as Redux or MobX

Update: I updated my code, so it can run standalone as a complete demo.
There is the codesandbox demo link

Sign up to request clarification or add additional context in comments.

9 Comments

Cheers. It does work nicely thank you. I think I shall take your suggestion and look to Redux though.
@Darren You're welcome. MobX requires minimal changes on your code structure, if you have a working project already. Redux has better support, and more suitable for a bigger project from the start.
Thank you. I think i'll need some time researching.
In the interim - my <span className="modalClose"><a onClick={this.toggle.bind(this)} href="javascript:;"><svg>...</svg></a></span> is now not working. What is the best way to do this? Cheers
@Darren What behavior do you expect? How about add a callback function to props, and then call footerTriggeredHandler just like footer to trigger the toggle?
|
0

You need to pass the toggle() method to your <Footer /> component

import React from 'react'
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
import Footer from './Footer';

class Newsletter extends React.Component {
    constructor(props) {
      super(props);
      this.state = {addClass: false}
    }
    toggle() {
      this.setState({addClass: !this.state.addClass});
    }
    render() {
      let toggleModal = ["newsletterModal"];
      if(this.state.addClass) {
        toggleModal.push('active');
      }
      return(
            <div className={toggleModal.join(' ')}>
                <div className="newsletterContainer">
                   <Footer onClick={this.toggle.bind(this)} />                     
                   <content /> 
                </div>
            </div>
        );
    }
}
export default Newsletter;

In your Footer.js file use the onClick function passed in as prop

import React from 'react'
import PropTypes from 'prop-types';
import Link from 'gatsby-link'
const Footer = (props) => (
    <footer>
        <a onClick={this.props.onClick} href="javascript:;">
            Newsletter
        </a>        
    </footer>
)
export default Footer

3 Comments

Thank you. I think I need to explain better as I do not want the <footer /> to be brought into the modal. Just a trigger within the <footer /> to add the .active class to newsletterModal
Then you should store the state of addClass in the parent component that renders the newsletter modal and the footer and pass that down to newsletter modal
Thank you @yanhua. I have updated my question to further explain. I have previously tried your suggestion of within the parent, yet it failed because of another trigger being added to open the menu. Is their a way to combine the two?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.