Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.PropType with two possible shapes

Background: I'm trying to specify a prop type for a dimension spec where I can either specify an explicit value, or a min and max value to evaluate. This will be used by an image component that will evaluate if the image is an acceptable height and width for user generated input.

The abstract goal is to have a React props value resolve to one of two possible shapes.


Each of the shapes work on their own

an explicit value for width would have this shape:

{
    width: PropTypes.shape({
        value: PropTypes.number.isRequired,
        error: PropTypes.string
    })
}

a min and max value would have this shape:

{
    width: PropTypes.shape({
        min: {
            value: PropTypes.number.isRequired,
            error: PropTypes.string
        }, max: {
            value: PropTypes.number.isRequired,
            error: PropTypes.string
        }
    })
}

EDIT: As pointed out in the accepted answer, there is a bug in this block that I did not catch. For some reason it was not giving me a run-time error, but this is not a working example of nested shapes, see the answer for the correct syntax.

If I try either of these methods and the properties match, everything works as expected so I can safely assume errors are coming from the code below.


What does not work

I tried using an optionalUnion as seen in the React typechecking documentation via PropTypes.oneOfType with the syntax below.

{
    width: PropTypes.oneOfType([
        PropTypes.shape({
            value: PropTypes.number.isRequired,
            error: PropTypes.string
        }), PropTypes.shape({
            min: {
                value: PropTypes.number.isRequired,
                error: PropTypes.string
            }, max: {
                value: PropTypes.number.isRequired,
                error: PropTypes.string
            }
        })
    ])
}

When I try this, I get the response

Warning: Failed propType: checker is not a function Check the render method of 'MyParentContainer'.

If I use either shape without the PropTypes.oneOfType method, no complaints in the debugger, everything runs as expected. PropTypes.oneOf throws a seemingly worse error:

warning.js:45Warning: Failed propType: Invalid prop 'dimensions.width' of value '[object Object]' supplied to 'MyComponent', expected one of [null,null]. Check the render method of 'MyParentContainer'.

Does anyone know if this syntax is off or is this a limitation of PropTypes.oneOfType? Is there a standard solution to this kind of problem?

like image 430
Shane Avatar asked Nov 29 '16 20:11

Shane


People also ask

What is React PropType array with shape?

In this article we will learn how to use React PropType array with shape. We use React PropTypes for type checking of variables in react. It makes debugging much easier and quicker.

How do you define an object PropType in React?

propTypes = { //// key is the name of the prop and // value is the PropType } export default Count; PropTypes are also objects with a key and a value pair where the 'key' is the name of the prop while the value represents the type or class by which they are defined.

What is shape in PropTypes?

PropTypes. shape is used when describing an object whose keys are known ahead of time, and may represent different types. const shapeProp = { name: 'Jane', age: 25 } // PropTypes.shape({ name: PropTypes.string, age: PropTypes.number })

Can we use PropTypes in functional component?

Firstly, npm install / yarn add the prop-types package if you haven't already. Then, add your propTypes (and defaultProps too if required) after the stateless functional component has been defined, before you export it.


1 Answers

PropTypes.shape is not recursive, you have to repeat it for substructures, e.g.

width: PropTypes.shape({
    min: PropTypes.shape({
        value: PropTypes.number.isRequired,
        error: PropTypes.string
    }).isRequired,
    max: PropTypes.shape({
        value: PropTypes.number.isRequired,
        error: PropTypes.string
    }).isRequired
})

Therefore, the entire validator should be:

const propTypes = {
    width: PropTypes.oneOfType([
        PropTypes.shape({
            value: PropTypes.number.isRequired,
            error: PropTypes.string
        }),
        PropTypes.shape({
            min: PropTypes.shape({
                value: PropTypes.number.isRequired,
                error: PropTypes.string
            }).isRequired,
            max: PropTypes.shape({
                value: PropTypes.number.isRequired,
                error: PropTypes.string
            }).isRequired
        })
    ])
};

Also, I really recommend to use variables to increase readability and prevent code duplication:

const valueType = PropTypes.shape({
    value: PropTypes.number.isRequired,
    error: PropTypes.string
});

const propTypes = {
     width: PropTypes.oneOfType([
         valueType,
         PropTypes.shape({
             min: valueType.isRequired,
             max: valueType.isRequired
         })
     ])
 };
like image 199
Sulthan Avatar answered Nov 03 '22 07:11

Sulthan