Passing the same props while rendering different elements conditionally in React.js

时间:2018-03-25 19:45:09

标签: javascript reactjs chart.js

I have a React Component named Chart. I want to render different charts in it based on the prop passed from their parent, but having the exactly same config. What is the best way to do it?

class Chart extends Component {

  static propTypes = {
    type: PropTypes.string.isRequired
  }

  state = {
    chartData: {
      // some data here 
    }
  }

  render(){
    return(
      this.props.type === 'line' ?
      { <Line
        data={this.state.chartData}
        options={{
          title: {
            display: true,
            text: 'Cities I\'ve lived in',
            fontSize: 25
          },
          legend: {
            display: true,
            position: 'right',
            labels: {
              fontColor: '#000'
            }
          }
        }}
      /> } : this.props.type === 'bar' ?
      { <Bar
          //same stuff here as <Line />
        />
      } : this.props.type === 'pie' ?
      { <Pie
          //same stuff here as <Line />
        /> }
      }
    );
  }
}

Don't think it matters since the question is generic, but I'm using react-chartjs-2 / chart.js 2 library for rendering charts.

Note: I've tried using a variable name in place of Line/Bar/Pie, but it doesn't work if I'm using JSX or rendering a non-html tag. Better ways to solve it while using JSX and non-html tags are also welcome.

3 个答案:

答案 0 :(得分:1)

Two things:

  • Use stateless component, in your example you are not using state.
  • Use switch instead of if statements.

Your new component, ...chart are the destructured props. Read more about destructuring on MDN.

// Chart.js - new component

export const Chart = ({ type, ...chart }) => { 
  switch(type) {
    case 'line':
      return <Line {...chart} />
    case 'bar':
      return <Bar {...chart} />
    case 'pie':
      return <Pie {...chart} />
    default:
     return null;
  }
}

Example usage

// App.js 

render() {
  return (
    <div>
      <Chart type="line" options={optionsObject} whateverProp="whatever" />
      <Chart type="bar" options={optionsObject} whateverProp="whatever" />
      <Chart type="pie" options={optionsObject} whateverProp="whatever" />
    </div>
  )
}

答案 1 :(得分:0)

import React, { Component } from 'react';

class Chart extends Component {
  components = {
    Line: Line,
    Bar: Bar,
    Pie: Pie
  };
  render() {
    const TagName = this.components[this.props.tag || 'Line'];
    return
      <TagName
        data={this.state.chartData}
        options={{
        title: {
          display: true,
          text: 'Cities I\'ve lived in',
          fontSize: 25
        },
        legend: {
          display: true,
          position: 'right',
          labels: {
            fontColor: '#000'
          }
        }
      }}
    />
  }
}
export default Chart;

// Call MyComponent using prop tag with required prop Line or Bar or Pie
<Chart tag='Line' />

NOTE:- You could create it as a stateless functional component as well.

答案 2 :(得分:0)

I prefer to have each chart as a component, so you can do chart's specific configuration in that component and your code will be more close to React codes modularity goal, so for Line chart as example:

class LineChart extends Component {

const chartLegend = {
            display: true,
            position: 'right',
            labels: {
              fontColor: '#000'
            }};

const chartTitle = {
            display: true,
            text: 'Cities I\'ve lived in',
            fontSize: 25
          };
const chartOptions = {
          title: chartTitle ,
          legend: chartLegend           
        }

  render(){
    return(
      <Line
        data={this.props.chartData}
        options={chartOptions }
      /> 
    );
  }
}

Now for chat Component as container component you can use switch case or guard conditions as @loelsonk also said, I prefer to have guard conditions besides switch:

class Chart extends Component {

  static propTypes = {
    type: PropTypes.string.isRequired
  }

  state = {
    chartData: {
      // some data here 
    }
  }

  render(){
    if (this.props.type === 'line')
      return <LineChart chartData={...}/>; 
    if (this.props.type === 'bar')
      return <BarChart chartData={...}/>;
    if (this.props.type === 'pie')
      return <PieChart chartData={...}/>;      
  }
}

this way you can easily replace any chart implementation any time that needed.