Let's continue with the earlier example where we displayed company profile of AAPL. Let's say I want to display the Company Profile for FB at some other place in the application. Since we have two variables ticker and companyProfileInfo hard-coded inside this component should I copy and paste the entire component to some other place and replace the ticker and companyProfileInfo to be that of FB?

Well, no. Remember React is about building reusable Components. So we want to build a component called CompanyProfile that can be reused for any company ticker.

Let's look at the CompanyProfile component again. Here we have two variables ticker and companyProfileInfo. So what if instead of hard coding the values of these two variables here inside the component, we could pass those values to this component instead?

If we were to pass these values to this component from outside, then this Component will not be tied to one Company ticker. We can pass in XYZ and it's profile info to this component and it should be able to render the profile for XYZ or any other company for that matter. This component becomes truly reusable. Wonderful, that's what we want.

  1. function CompanyProfile(props) {
  2. //Instead of storing these variables we want to pass
  3. //these values to this component from outside.
  4. const ticker = //pass the value for this variable from outside
  5. const companyProfileInfo = //pass the value for this variable from outside
  6. return (
  7. <div>
  8. <div>Profile of: {ticker}</div>
  9. <div>
  10. {
  11. Object.keys(companyProfileInfo)
  12. .map((key, index) => {
  13. return <div>{key}: {companyProfileInfo[key]}</div>
  14. })
  15. }
  16. </div>
  17. </div>
  18. )
  19. }

Well then how do we pass these values to a component from outside? That's where props comes in. In React lingo, props are something that the component is passed by the user of that component (parent component passes props to child component).You can pass anything as a props - function, object, boolean, string, number etc. Here's an example of a Component passing the props to its children.

  1. function Children(props) {
  2. return (
  3. <div>{props.textToDisplay}</div>
  4. )
  5. }
  6.  
  7. function Parent(props) {
  8. return (
  9. <Children textToDisplay={'Hello'}></Children>
  10. )
  11. }

There are couple things going on here. First - remember on the first page of this tutorial we said that we can compose components (we can use one component inside the other)? Well that's what we are doing here. Parent component uses Children component inside it's return.

Second - if you inspect the above code snippet carefully, we see that when the Parent uses the Children (inside return) it's also passing something called textToDisplay with some value Hello. We call this "passing the props". So Parent is passing a props called textToDisplay to the Children. How, then, does the Children use the value that the Parent passes down to it?

  • Component created with function

If you created your component as a function like we did here, all the props its Parent passed down will be accessible through the argument. If you look at the Children above it's using props.textToDisplay inside the div. All the props passed to the Children are passed as this single props argument. For example, if the Parent had passed a props called defaultValue, the Children would access it as props.defaultValue.

  • Component created as React.Component

If you created your Component by extending React.Component then all the props would be available as this.props. The equivalent of above Children function using React.Component would look like this:

  1. class Children extends React.Component {
  2. render(){
  3. return (
  4. <div>{this.props.textToDisplay}</div>
  5. )
  6. }
  7. }

Now that we know what props are, lets do some exercise and see how we can make CompanyProfile component reusable.

One thing you must remember regarding props is that you should never mutate props - React will complain if you do. This is something given to the component by it's parent - accept with love and don't try to mess around with things to make your parent angry!

  1. function Children(props) {
  2. //❌ NEVER DO THIS
  3. props.textToDisplay = 'I want to mutate props'
  4. return (
  5. <div>{props.textToDisplay}</div>
  6. )
  7. }

PropTypes

In many cases it's better for a component to clearly define a contract regarding the props it can accept - data type, data structure, if the props is required etc.There are couple obvious benefits of this:

  • React can enforce type checking to avoid many bugs arising from parents passing props with a type that's different from what the children expects (ex. parent passing string when children expects an object).
  • If you are writing components that will be used by different people at different parts of the application, it's always useful for those users to know what are the props they can pass, what is the expected structure of the props etc.To define this contract - first you need to add prop-types as a project dependency (provided by React team) and you need to define a special property called propTypes in your component.
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3.  
  4. class SoftwareEngineer extends React.Component {
  5. render(){
  6. return (...)
  7. }
  8. }
  9.  
  10. //defines "propTypes" property in this component
  11. SoftwareEngineer.propTypes = {
  12. name: PropTypes.string.isRequired, //expects string and is required
  13. hobbies: PropTypes.arrayOf(PropTypes.string), //expects array of string
  14. address: PropTypes.shape({
  15. street: PropTypes.string,
  16. city: PropTypes.string
  17. }) //must be an object with 'street' and 'city' fields
  18. }

Here we have defined the propTypes property and assigned an object. Each key in this object represents the name of the props the user of this component can pass. The value defines the "type" of the props - you know string, number, array etc. All props are optional (user of the component doesn't have to pass them) except the one that has .isRequired. Here's a quick explanation on three props defined above:

  • name - It expects the value of this props to be a string and it's required because, well, it has .isRequired.
  • hobbies - It's optional but if passed it must be an array of strings.
  • address - It's also optional but if passed it must be an object with two fields - street and city and both must be string.These are just some examples of what you can do to enable type checking. There are plenty more types you can define - please check out the documentation for more.

Default Props

In some cases you might want to define a default value for a props in case it is not passed to you.You can use defaultProps property to define your defaults. With this you're basically saying - "if someone doesn't pass me a value for a props that I'm expecting, then I want the value of that props to be what I have defined in the defaultProps". For example - for above component we can define defaultProps as follows:

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3.  
  4. class SoftwareEngineer extends React.Component {
  5. render(){
  6. //if this props is not passed, it will print default value as defined by `defaultProps`
  7. console.log(this.props.hobbies);
  8.  
  9. //if this props is not passed, it will print `undefined` because we haven't defined any default value for this props
  10. console.log(this.props.address);
  11. return (...)
  12. }
  13. }
  14.  
  15. //defines "defaultProps" property in this component
  16. SoftwareEngineer.defaultProps = {
  17. hobbies: ['Writing React code']
  18. }

Let's say if the user of this component doesn't pass any value for hobbies, then it will be defaulted to ['Writing React code'].And if the user of the component doesn't pass any value for address then it will resolve to undefined because we haven't defined the default value for it.