Gilad Barner
Gilad Barner

Reputation: 741

React.js - use DOM elements from different levels of component hierarchy

We need to perform some actions related to DOM elements in different parts of the component hierarchy, on window.onresize and window.onscroll:

1) Moving elements when resizing to mobile resolution

2) Changing classes and styles when scrolling.

The problem is that the elements are in different levels of the React component hierarchy, i.e. nested in different components. Thus if I understand correctly, I can't use refs to access the DOM nodes.

I have: {someCondition ? <FirstComponent/>: <SecondComponent/>}

Whereas someCondition can change either due to user UI actions, or it could be true from the beginning of the page load.

I've tried using componentDidMount and componentDidUpdate on my FirstComponent, but found that componentDidMount only fires if someCondition is true from the beginning, and componentDidUpdate indeed fires when someCondition changes, but then the required DOM elements (contained in the component) are not ready at that point, and document.getElementById fails.

Adding window.requestAnimationFrame did not help.

I imagine there should be a solid way to do this in React?

Maybe React.findDomNode?

Thank you.

Upvotes: 3

Views: 1127

Answers (1)

Brandon
Brandon

Reputation: 39182

What you are describing is the antithesis of React. It is an approach I would expect when using an imperative jQuery/Dojo based framework.

With React, you are developing functional components that know how to convert state into rendered HTML and you need to approach the problem differently.

For your problem, your window.onresize and window.onscroll callbacks should not try to manipulate the React DOM elements. Instead it should just update state and let React re-render. Your React components should receive this state and render accordingly.

Here's a simple example where the main app listens to these events and updates its state, which triggers a re-render. The app passes the state as props to the children. The children use the props to conditionally change their css class names. They could easily render different html or inline styles as well.

const Component2 = ({someCondition2}) => {
    const className = someCondition2 ? "foo" : "bar";
    return <div className={className}>Hello</div>;
};

const Component1 = ({someCondition1, someCondition2}) => {
    const className = someCondition1 ? "A" : "B";
    return (
        <div className={className}>
            <Component2 someCondition2={someCondition2} />
        </div>
    );
};

class App extends React.Component {
    state = {
        someCondition: false,
        someCondition2: true,
    };

    componentDidMount() {
        window.onresize = ev => {
            const someCondition = ev.xxx;
            // re-render with the new state
            this.setState({ someCondition });
        };

        window.onscroll = ev => {
            const someCondition2 = ev.xxx;
            this.setState({ someCondition2 });
        };
    }

    render() {
        const {someCondition, someCondition2} = this.state;
        return (
           <Component1
              someCondition1={someCondition1}
              someCondition2={someCondition2} />
        );
    }
}

React.render(<App />, document.getElementById("container"));

Upvotes: 4

Related Questions