I am new to React and have been tasked with updating an existing component (a form). My task is to read a setting from JSON and show or hide an element based on that setting.
The JSON appears like:
"forms": {
"enquiry": {
"showConfirmCheckbox": "true"
},
},
Using and existing component that already reads this JSON and turns it into context which is called config, I can use the above as such:
In my propTypes I have:
static propTypes = {
config: PropTypes.object.isRequired,
}
And in my render function I just wrap the element in a conditional statement:
{config.forms.enquiry.showConfirmCheckbox &&
<Checkbox
etc, etc, etc
This works fine. However, I want to add a default for this value just in case the original JSON file is not updated correctly or is missing that entry. To that effect I added this to my defaultProps:
static defaultProps = {
config: {
forms: {
enquiry: {
showConfirmCheckbox: 'true',
},
},
},
}
However, this doesn't seem to work.
If I delete the line 'showConfirmCheckbox': 'true', from the JSON file the if condition still validates as false - it doesn't pick up the defaultProps value.
Would anyone know how to amend this? Is there something I am doing wrong here?
Are there other entries to your config, which are being passed while the showConfirmCheckbox isn't? The config prop is treated as a whole, if any object is passed, default props are not applied.
Docs on propTypes
There's no way you can default nested props because under the hood , defaultProps are only shallow merged with first-level props if props are missing.
, click for more information
However, in order to get around this, you may consider adding a few customized default logics in both lifecycle HOOK componentWillReceiveProps and constructor
And maybe the nested propTypes can also be helpful.
propTypes: {
configs: PropTypes.shape({
forms: PropTypes.shape({
enquiry: PropTypes.shape({
showConfirmCheckbox: PropTypes.bool.isRequired
})
})
})
}
Related
I'm currently building a large component that should only take two components as children (in this case, a TopBar and a NavBar). So far, I've tried specifying the propType for the children as this:
AppHeader.propTypes = {
/**
* AppHeader should ONLY accept TopBar and NavBar components as children.
*/
children: PropTypes.arrayOf([
PropTypes.instanceOf(TopBar),
PropTypes.instanceOf(NavBar),
]),
};
However, the PropTypes aren't working for me. I'm facing this problem with a similar component that can take a random number of two types of subcomponents.
How should I go about creating this PropTypes for the children where you specify the type of components that it can take. (This is not an enum since an enum is 'either or'/ oneOf).
Thanks so much in advance!
I didn't test this but it seems that PropTypes.arrayOf doesn't accept an Array. Instead try providing the PropTypes.oneOfType object. Then pass your array into that.
AppHeader.propTypes = {
children: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.instanceOf(TopBar),
PropTypes.instanceOf(NavBar),
])
),
};
references:
https://reactjs.org/docs/typechecking-with-proptypes.html#proptypes
https://blog.logrocket.com/validating-react-component-props-with-prop-types-ef14b29963fc/
I have a functional component below which has props coming from parent component and I added propTypes for the whole props object. However, the lint fails with the below error message.
9:16 error 'data' is missing in props validation react/prop-types
9:22 error 'data.text' is missing in props validation
Excerpt from code
import PropTypes from 'prop-types'
const Child = (props) => <div>{props.data.text}</div>
Child.propTypes = {
props: PropTypes.object
}
Could anyone please help?
As discussed in the comments, you have a few issues:
PropTypes.object is a very vague type declaration, and most linters will ding you for that
You are referencing properties of that object inside of your functional component that are not declared in your proptypes
You are using your prop declaration to attempt to refer to the props argument as a whole, rather than the properties within.
A more correct way to write all of this would be like so:
import PropTypes from 'prop-types';
/* Note we destructure `data` directly from props in the args */
const Child = ({ data }) => (<div>{data.text}</div>);
Child.propTypes = {
data: PropTypes.shape({ // use `shape` to allow us to declare types of properties within
text: PropTypes.string, // define the text property's type
}),
}
Additionally, you may want to define some of these items as required, or provide default entries.
From Alexander Nied solution, I made it work writing the below way without using shape, I think it is more generic.
const Child = ({ data }) => (<div>{data.text}</div>);
Child.propTypes = {
data: PropTypes.object
}
I really need help with this and have been stuck for days. (It's also my first question so don't roast me.)
I've been trying to build my Quasar app, but all my TypeScript errors scream about Unsafe (assignment |member access | call) on an 'any' value I've narrowed it down to the fact that the this.$attrs that I use to pass the URL into my component has a type: any, but I have no idea how to set a type on the $attrs attribute and haven't found anything about how to do that. If someone could help that'd be amazing.
Basically, what my app is trying to do is grab the slug attribute, and based on that it'll look up the appropriate page in my props. My props are populated with posts from the Ghost Blog API.
Here's an example block of where my errors are:
import Vue from 'vue'
import { mapState } from 'vuex';
import GhostModule from '../store';
import { PostOrPage, PostsOrPages } from '#tryghost/content-api';
export interface WhyPageState {
page: PostOrPage;
slug: PostOrPage['slug'];
}
declare module 'vue/types/vue' {
interface Vue {
$attrs: {
slug: string
}
}
}
export default {
name: 'Page',
created() {
this.slug = this.$attrs.slug; // unsafe assignment of an any value
},
computed: mapState({
page(state: typeof GhostModule) {
const result: PostsOrPages = state?.GhostModule?.pages.filter(
page => page.slug === this.slug // Unsafe member access on any value, Unsafe member access .slug on an any value
);
return result[0]; // Unsafe return of an any typed value, Unsafe member access[0] on an any value
}
})
};
If anyone has a solution on how to define types to $attrs or another solution entirely on how to populate ghost page data dynamically, it'd be appreciated.
I use this vue-multiselect plugin: https://vue-multiselect.js.org/
Since I'm going to use it many times, I want to set some defaults props.
For example I want the selectLabel prop to be an empty string, and maybe to sets some <slot>s.
In a typical jQuery plugin I could use $.plugin.defaults to set the defaults. How to do the same with vue plugins?
You can use extends to subclass your component. Pass it a spec just like you would use when defining a component, but only supply the things you want to provide defaults for.
<script>
import Multiselect from 'vue-multiselect';
export default {
extends: Multiselect,
props: {
selectLabel: {
type: String,
default: ''
}
}
}
</script>
Say I have the following condition:
static propTypes = {
deployment: PropTypes.shape({
getMetrics: PropTypes.func.isRequired,
getRootMetric: PropTypes.func.isRequired
}).isRequired,
}
How would I declare the default for deployment ? Would it be undefined or would it be [] ?
Since the prop is required, I see no reason to define a default. Defaults are only useful for optional props which also have a reasonable default value, such as an empty list [] or an empty object {}.
You could not define a default prop or you could just say {getMetrics: () => {}, getRootMetric: () => {}} since it's an object. However, required props don't really need defaults, otherwise they wouldn't really be required.
To give an example :
You build a small npm module that comes along with a config object
You can define this object as required (the module would break
without users providing it)
You can provide defaults (it would work with these if not provided)
So, finally, it's up to you if and how you provide defaults.
I would use something like the below:
SomeComponent.defaultProps = {
deployment: {
getMetrics: () => {},
getRootMetric: () => {}
}
}
Defaults are good when not having the values would break your app.