Apply condition on specific nested child elements in ReactJS

zeyadetman

Zeyad Etman

Posted on February 16, 2020

Apply condition on specific nested child elements in ReactJS

Published here: Apply condition on specific nested child elements in ReactJS

Apply condition on specific nested child elements in ReactJS

Intro

We have a reactjs app, this app contains a container component called App inside it, we have multiple nested components some of them contains button elements. What we want to do is disable all child buttons programmatically without using styles way when I toggle a switch button in the container component App.

Naïve Solution

The naïve solution to this problem is simple create a new state to store the value of the switch button, then iterate manually over each child components and searching for button, if you find a button pass the switch button state value to the component to update the button behavior whenever the switch changes <Button {...(isDisable ? { disabled: true } : {})} />.

If we have 10 nested buttons for example, it'll be hassle to iterate over them, also we may add more buttons or change the condition.

take a look of the Tab 1 Panel 1 button here: https://github.com/zeyadetman/action-on-specific-child-elements/tree/naive-solution

Another Solution

Here's another solution to do, using useRef to get a reference to the component App then get all buttons inside it here's a code for this:

const appRef = useRef(null);
useEffect(() => {
  // here we got all rendered buttons inside the component
  const appButtons = appRef.current.getElementsByTagName("button"); 
  for (let btn of appButtons) {
    if (!isEnabled) btn.disabled = true;
    else if (isEnabled) btn.disabled = false;
  }
}, [isDisable]);
return (
  <div ref={appRef}> 
Enter fullscreen mode Exit fullscreen mode

In previous useEffect we got rendered buttons only not them all, and whenever a change happens the useEffect won't listen to it.

Hack it

We've to check if there's change in DOM, then call the body of the useEffect again, So we'll create two states to handle the tabs and subTabs changes, they're only elements to change the DOM, then pass their values to the dependancy array of the useEffect to listen the changes:

const [tabSelected, selectTab] = useState("1");
const [subTabSelected, selectSubTab] = useState("1");
.
.
.
useEffect(() => {
  // here we got all rendered buttons inside the component
  const appButtons = appRef.current.getElementsByTagName("button"); 
  for (let btn of appButtons) {
    if (!isEnabled) btn.disabled = true;
    else if (isEnabled) btn.disabled = false;
  }
}, [tabSelected, subTabSelected, isDisable]);
Enter fullscreen mode Exit fullscreen mode

Conclusion and corner cases

I think this solution is fit only when you have multiple nested element and you want to apply condition on them all without iterating over them manually.

Corner cases

  • If you want to exclude button from our previous app, you can add a class to it then check for it in the useEffect function.

  • If the component will re-render and get back to its initial state, then you have to return a function in the useEffect to undo whatever you did in the useEffect body it's componentWillUnmount in React Lifecycle.

Here's the final commit to the app https://github.com/zeyadetman/action-on-specific-child-elements/tree/final-commit

💖 💪 🙅 🚩
zeyadetman
Zeyad Etman

Posted on February 16, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related