2

Given the following QueryRenderer component:

class ProjectQueryRenderer extends Component {
  constructor(props) {
    super(props);
    this.renderProjectList = this.renderProjectList.bind(this);
  }

  renderProjectList({ error, props }) {
    if (props) {
      return (
        <ProjectList
          connection={props.viewer.allProjects}
          onSelectProject={this.props.onSelectProject}
          selectedProject={this.props.selectedProject}
        />
      );
    }
  }

  render() {
    return (
      <QueryRenderer
        environment={environment}
        query={ProjectsQuery}
        render={this.renderProjectList}
      />
    );
  }
}

ProjectQueryRenderer.propTypes = {
  onSelectProject: Proptypes.func.isRequired,
  selectedProject: Proptypes.string.isRequired,
};

The problem I have is that renderProjectList is not executed again when my own selectedProject prop changes value. The render method obviously does get triggered, but since neither any of the props of QueryRenderer is changed, renderProjectList is not invoked either.

What is the best approach to handle this?

nickdecooman
  • 153
  • 11

2 Answers2

0

Instead of passing a render function into QueryRenderer, you can pass in the whole ProjectList component as a property along with any props that component needs to render from the parent. They extra props show up in QueryRenderer as other. See below:

class ProjectQueryRenderer extends Component {
  render() {
    return (
      <QueryRenderer
        environment={environment}
        query={ProjectsQuery}
        component={ProjectList}
        onSelectProject={this.props.onSelectedProject}
        selectProject={this.props.selectedProject}
      />
    );
  }
}

ProjectQueryRenderer.propTypes = {
  onSelectProject: Proptypes.func.isRequired,
  selectedProject: Proptypes.string.isRequired,
};

and now QueryRenderer

class QueryRenderer extends Component {

  render() {
    const {environment, query, component, ...other} = this.props 

    // parameters to component that QueryRenderer computes
    // based on environment, query, etc...
    // plus also pass in all the extra props that are coming from
    // ProjectQueryRenderer (like onSelectProject)
    const computedProp = //something

    return (
     <div>
       <component computedProp={computedProp} {...other} />
     </div>
   )
  }
}

This way, both ProjectQueryRenderer and QueryRenderer can pass props into a arbitrary component into QueryRenderer and you can reuse QueryRenderer will other types of components as well.

aherriot
  • 4,495
  • 6
  • 24
  • 36
  • Not sure I understand this correctly. The example above gives an `Uncaught TypeError: this.props.render is not a function` error. – nickdecooman Aug 20 '17 at 06:05
  • I changed the `render` prop name to `component` but it's up to you to find an appropriate name for it. Maybe my solution is not entirely what you are looking for. I am looking at it again and I am working. Does `ProjectList` depend on properties that are past to it both from `ProjectQueryRenderer` and also `QueryRenderer`? If so there is another solution to this problem. – aherriot Aug 20 '17 at 12:34
0

I found a solution that successfully rerenders my list component (without performing an api request) each time one of my injected props changes. I had to define my component in the render function so that I could access the new prop values.

Below the result as a functional component:

const ProjectQueryRenderer = ({ onSelectProject, selectedProject }) => {
  const comp = ({ error, props }) => {
    if (props) {
      return (
        <ProjectList
          connection={props.viewer.allProjects}
          onSelectProject={onSelectProject}
          selectedProject={selectedProject}
        />
      );
    }
  };
  return (
    <QueryRenderer
      environment={environment}
      query={ProjectsQuery}
      render={comp}
    />
  );
};
nickdecooman
  • 153
  • 11
  • The downside to this approach is that you create a new definition for the `comp` function every time `ProjectQueryRenderer` needs to render. – aherriot Aug 20 '17 at 12:40