Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rendering React Components in a Specified Order

Having a bit of a hard time finding anything about this specific pattern, and I'm not even exactly sure how to describe it in search terms so apologies if this is a duplicate question (although I don't think it is).

I want to output a React layout based on an order passed to the app that the user can set via a settings panel. The idea is that there are a few different containers to output on the page that I want the user to be able to re-arrange. It's important to note that this order is not changeable after the app renders. (I want the user to be able to say "Show me PanelA, PanelC and PanelB in that order")

Now, I've figured out how to accomplish this using the following pattern:

// user-ordered array is passed to the app:
    const settings = {
        layout: [
            "panela",
            "panelb",
            "panelc"
        ]
    }

    class MyComponent extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                // some state
            }
        }
        renderComponentsInOrder() {
            return settings.layout.map(component => {
                switch(component) {
                    case "panela":
                        return 
                            <PanelA {.../* some state */} />
                    case "panelb":
                        return 
                            <PanelB {.../* some state */} />
                    case "panelc":
                        return 
                            <PanelC {.../* some state */} />
                    default: return null
                }
            })
        }

        render() {
            return this.renderComponentsInOrder()
        }
    }

but this strikes me as really inefficient. The order of the components shouldn't need to be re-calculated every time render runs because the order won't change while the app is running. I've tried doing things like memoizing the renderComponentsInOrder method to cache the calculated layout or renaming the values in the settings.layout array and calling them directly, but haven't been able to get anything to work because the components need to update based on state.

Any help or advice would be greatly appreciated. Thanks!

EDIT: Ideally I'm looking for a JS-based solution as compatibility is a bit of an issue and I don't want to rely solely on the browser's implementation of CSS.

like image 620
Dan Haddigan Avatar asked Jan 10 '18 20:01

Dan Haddigan


People also ask

Do components Render in parallel in ReactJS?

As you know React components render in parallel. There is no guarantee in order that they finish rendering. I want to render the following components in this exact order: . I need this header to render first so that I can act on it in my other two components after rendered.

How do I re-render React components?

React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically. However, there may be cases where the render () method depends on some other data.

What is a higher order component in react?

Higher-Order Component Pattern A higher-order component is a function that takes a component and returns a new component. In other words, HOC takes a React component and adds any extra cross-cutting logic to it, and returns a new React component with new behavior. It should remind you of the Decorator pattern:

What is render prop pattern in react?

Render Props Pattern 2.1 Definitions Render Props Pattern when a component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic. 2.2 Pattern structure


2 Answers

A slightly different take on the answer given by Brandon. You could have a function to generate the component based on the state/props:

const settings = {
    layout: [
        "panela",
        "panelb",
        "panelc"
    ]
};

const panels = settings.layout.map(c => {
  switch (c) {
    case "panela": return (props, state) => <PanelA key="a" foo={state.foo} />
    case "panelb": return (props, state) => <PanelB key="b" bar={state.bar} />
  }
});

// Now use panels array to render:

class MyComponent extends React.Component {
  render() {
    const props = this.props;
    const state = this.state;
    const ordered = panels.map(p => p(props, state));

    return <div>{ordered}</div>
  }
}
like image 69
Evan Trimboli Avatar answered Nov 03 '22 09:11

Evan Trimboli


Method 1:

Just transform the settings array into an array of Components once before you render:

const settings = {
        layout: [
            "panela",
            "panelb",
            "panelc"
        ]
    };

const panels = settings.layout.map(c => {
  switch (c) {
    case "panela": return { Component: PanelA, key: c };
    case "panelb": return { Component: PanelA, key: c };
    ...
  }
});

// Now use panels array to render:

class MyComponent extends React.Component {
  // ...
  renderComponentsInOrder() {
    return panels.map(({Component, key}) => (
      <Component key={key} {.../* some state*/} />
    ));
  }

  // ...
}

Method 2:

Just create a mapping table:

const settings = {
        layout: [
            "panela",
            "panelb",
            "panelc"
        ]
    };

const panelLookup = {
  "panela": PanelA,
  "panelb": PanelB,
   ...
};

// Now use lookup to render:
// Now use panels array to render:

class MyComponent extends React.Component {
  // ...
  renderComponentsInOrder() {
    return settings.layout.map(key => {
      const Component = panelLookup[key];
      return <Component key={key} {.../* some state*/} />;
    });
  }

  // ...
}
like image 36
Brandon Avatar answered Nov 03 '22 09:11

Brandon