0

I'm having troubles updating the header class so it updates it's className whenever displaySection() is called. I know that the parent state changes, because the console log done in displaySection() registers the this.state.headerVisible changes but nothing in my children component changes, i don't know what I'm missing, I've been trying different solutions for some hours and I just can't figure it out what i'm doing wrong, the header headerVisible value stays as TRUE instead of changing when the state changes.

I don't get any error code in the console, it's just that the prop headerVisible from the children Header doesn't get updated on it's parent state changes.

Thank you!

class IndexPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      section: "",
      headerVisible: true,

    }
    this.displaySection = this.displaySection.bind(this)
  }

  displaySection(sectionSelected) {
    this.setState({ section: sectionSelected }, () => {
      this.sectionRef.current.changeSection(this.state.section)
    })

    setTimeout(() => {
      this.setState({
        headerVisible: !this.state.headerVisible,
      })
    }, 325)

    setTimeout(()=>{
      console.log('this.state', this.state)
    },500)
  }

  render() {

    return (
      <Layout>
            <Header selectSection={this.displaySection} headerVisible={this.state.headerVisible} />
      </Layout>
    )
  }
}
const Header = props => (
  <header className={props.headerVisible ? 'visible' : 'invisible'}>
       <div className="navbar-item column is-size-7-mobile is-size-5-tablet is-uppercase has-text-weight-semibold">
              <span onClick={() => { this.props.selectSection("projects")}}>
                {" "}
                Projects
              </span>
  </header>
)
  • 1
    How do you verify that the class name doesn't change? FWIW, if you compute state based on existing state, always pass a function to setState: this.setState(state =>({headerVisible: !state.headerVisible})). – Felix Kling Apr 15 at 23:50
  • Using the React Developer Tools of Chrome I can see changes in props and states. I see that the state of IndexPage changes, but there is no change in Header props. – Meeseks Apr 15 at 23:58
  • Can you provide a Minimal, Complete, and Verifiable example? – Felix Kling Apr 16 at 0:00
  • You are never calling the selectSection function. Why don't you try calling it before return the page content? – Jacobo Tapia Apr 16 at 0:55
  • Yup, true, tried to simplify the code for the question and i ommited it, my bad – Meeseks Apr 16 at 8:01
1

There seemed to be a couple of issues with your example code:

  1. Missing closing div in Header
  2. Using this.props instead of props in onclick in span in Header

The below minimal example seems to work. I had to remove your call to this.sectionRef.current.changeSection(this.state.section) as I didn't know what sectionRef was supposed to be because it's not in your example.

class IndexPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      section: "",
      headerVisible: true,

    }
    this.displaySection = this.displaySection.bind(this)
  }

  displaySection(sectionSelected) {
    this.setState({ section: sectionSelected })

    setTimeout(() => {
      this.setState({
        headerVisible: !this.state.headerVisible,
      })
    }, 325)

    setTimeout(()=>{
      console.log('this.state', this.state)
    },500)
  }

  render() {

    return (
      <div>
            <Header selectSection={this.displaySection} headerVisible={this.state.headerVisible} />
      </div>
    )
  }
}

const Header = props => (
  <header className={props.headerVisible ? 'visible' : 'invisible'}>
       <div className="navbar-item column is-size-7-mobile is-size-5-tablet is-uppercase has-text-weight-semibold">
              <span onClick={() => { props.selectSection("projects")}}>
                {" "}
                Projects
              </span>
       </div>
  </header>
)

ReactDOM.render(
  <IndexPage />,
  document.getElementsByTagName('body')[0]
);
.visible {
  opacity: 1
}

.invisible {
  opacity: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

  • This worked, thank you! – Meeseks Apr 16 at 16:19
1

There is a markup error in your code in Header component - div tag is not closed. Also, I suppose, you remove some code to make example easy, and there is artifact of this.sectionRef.current.changeSection(this.state.section) cause this.sectionRef is not defined.

As @Felix Kling said, when you change the state of the component depending on the previous state use function prevState => ({key: !prevState.key})

Any way here is a working example of what you trying to achieve:

// @flow
import * as React from "react";
import Header from "./Header";

type
Properties = {};

type
State = {
    section: string,
    headerVisible: boolean,
};

class IndexPage extends React.Component<Properties, State> {

    static defaultProps = {};

    state = {};

    constructor(props) {
        super(props);
        this.state = {
            section: "",
            headerVisible: true,

        };
        this.displaySection = this.displaySection.bind(this)
    }

    displaySection(sectionSelected) {
        setTimeout(
            () => this.setState(
                prevState => ({
                    section: sectionSelected,
                    headerVisible: !prevState.headerVisible
                }),
                () => console.log("Debug log: \n", this.state)
            ),
            325
        );
    }

    render(): React.Node {
        const {section, headerVisible} = this.state;

        return (
            <React.Fragment>
                <Header selectSection={this.displaySection} headerVisible={headerVisible} />
                <br/>
                <div>{`IndexPage state: headerVisible - ${headerVisible} / section - ${section}`}</div>
            </React.Fragment>
        )
    }
}

export default IndexPage;

and Header component

// @flow
import * as React from "react";

type Properties = {
    headerVisible: boolean,
    selectSection: (section: string) => void
};

const ComponentName = ({headerVisible, selectSection}: Properties): React.Node => {
    const headerRef = React.useRef(null);

    return (
        <React.Fragment>
            <header ref={headerRef} className={headerVisible ? 'visible' : 'invisible'}>
                <div className="navbar-item column is-size-7-mobile is-size-5-tablet is-uppercase has-text-weight-semibold">
                    <span onClick={() => selectSection("projects")}>Projects</span>
                </div>
            </header>
            <br/>
            <div>Header class name: {headerRef.current && headerRef.current.className}</div>
        </React.Fragment>
    );
};

export default ComponentName;
  • Thank you very much! – Meeseks Apr 16 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.