reactjsstorybook

Setting Default Property Values in React Component


Trying to set the default value for some of my component properties. Doing a simple string or boolean works just fine, but I can't find any examples showing something more complex. I've tried a few things to apply defaults to the prepend property without success:

export default function ListItem({
  hasPrepend = false,
  prepend = { background: "bg-success", icon: { style:"fas", name: "fa-star" } },
  children,
}) {
  return(
    <div>
      {hasPrepend && <div className={`
        ${prepend.background}
        `}
      >
        <Icon style={prepend.icon.style} name={prepend.icon.name} />
      </div>
    }
    <div>
        {children}
    </div>
</div>
);
}

I've also tried

const defaultPrepend = {
  background: "bg-success",
  icon: {
    style: "fas",
    name: "fa-user",
    size: "fa-lg",
  }
};

export default function ListItem({
...
     prepend = defaultPrepend,
...
}) {

Am I even close?


Solution

  • Using the : reassigns the variable to a different name. This is useful if you want to expose the prop as a name that makes more sense outside of the component, but within the component that name would cause naming conflicts. It's a bit of a weird syntax, but you have to use the = sign to set a default, despite it being part of an object.

    This:

    export default function ListItem({
      hasPrepend = false,
      prepend = { background: "bg-success", icon: { style:"fas", name: "fa-star" } },
      children,
    }) {
    

    Note: Below there is a : after prepend. The colon allows you to deconstruct the 'prepend' object, and then assign default values to the nested properties.

    Would become this:

    export default function ListItem({
      hasPrepend = false,
      prepend: { background = "bg-success", icon: { style = "fas", name = "fa-star" } },
      children,
    }) {
    

    This example above will assign default values to 'background' and 'style', but a 'prepend' object is now required or it will throw an error (I think. I didn't actually test this).

    If you instead want to assign an entire default object to 'prepend', you can use the equal sign there, but then the nested properties within that object are assigned the same way as a regular javascript object, with colons like this:

    export default function ListItem({
      hasPrepend = false,
      prepend = { background: "bg-success", icon: { style: "fas", name: "fa-star" } },
      children,
    }) {
    

    If you want to live on the edge, you can do something like this to accomplish a bit of both:

    export default function ListItem({
      hasPrepend = false,
      prepend: { background = "bg-success", icon: { style = "fas", name = "fa-star" } = {
       icon: {} // Required, since you're deconstructing icon above. The rest will be populated by the default values.
    },
     },
      children,
    }) {
    

    But if you wanted to name the background property something more specific, like buttonBackground, that would be:

    export default function ListItem({
      hasPrepend = false,
      prepend = { background: btnBackground, ...},
      children,
    }) {
    

    Putting it all together

    Change this:

    export default function ListItem({
      hasPrepend = false,
      prepend = { background: "bg-success", icon: { style:"fas", name: "fa-star" } },
      children,
    }) {
      return(
        <div>
          {hasPrepend && <div className={`
            ${prepend.background}
            `}
          >
            <Icon style={prepend.icon.style} name={prepend.icon.name} />
          </div>
        }
        <div>
            {children}
        </div>
    </div>
    );
    }
    

    To this:

    const defaultPrepend = { background: "bg-success", icon: { style:"fas", name: "fa-star" } }
    
    export default function ListItem({
      hasPrepend = false,
      prepend: _prepend = defaultPrepend,
      children,
    }: any) {
    
        const prepend = {
            ...defaultPrepend,
            ..._prepend,
            icon: {
                ...defaultPrepend.icon
                ..._prepend.icon,
            }
        }
    
      return(
        <div>
          {hasPrepend && <div className={`
            ${prepend.background}
            `}
          >
            <div style={prepend.icon.style} className={prepend.icon.name} />
          </div>
        }
        <div>
            {children}
        </div>
    </div>
    );
    }