Metadata
- File Name:
2024-06-02 - Learning Tamagui Fundamentals - URL: Gist - dominicstop/2024-06-02 - Learning Tamagui Fundamentals.md
-
Introduction
@tamagui/core- Docs: React style and design system library.- The style API (i.e.
styledfunction) is similar tostyled-components(css-in-js). - It is similar to
react-native-web, in that it supports view and text RN components on the web (docs: "supports the full React Native API surface [in the web]"). - The styling api supports: media queries (e.g.
minWidth,maxHeight), pseudo selectors (e.g. css psuedo-classes like:hoverand:focusstates), container queries (e.g. apply styles to an element based on the size of its container), and other selectors.
- The style API (i.e.
-
Configuration: Custom themes, tokens, shorthands, and media queries.
- It all starts with creating a tamagui config:
tamagui.config.ts.- The
createTamaguifunction is declared increateTamagui.ts. - The first and only parameter named
configIn, and accepts an object of typeCreateTamaguiProps.CreateTamaguiPropsseems to define the configs accepted by thecreateTamaguifunction (i.e. because it matches the properties mentioned in docs, e.g. shorthands, media, token etc).- There are some properties that are remapped from a type called:
GenericTamaguiConfig(i.e.media,fonts, andtokens). - There is a optional property called
unsetthat accepts values of type:BaseStyleProps. - There is a optional property called
animationsthat accepts values of type:animations?: AnimationDriver<any>.
createTamaguireturns a type ofInferTamaguiConfig<CreateTamaguiProps>.InferTamaguiConfigaccepts a generic parameter namedConfthat extendsConfProps.- The
ConfPropstype accepts generic parameters "A to I", and remaps them to become a value in an object.- E.g.
Aremaps totokens?: A,Bremaps tothemes?: B, and so on. - So
ConfPropsis an object w/ the ff. properties:tokens,themes,shorthands,media,animations,fonts,onlyAllowShorthands,defaultFont, andsettings. - These look like the "shape" for a tamagui config. But i don't know why it's defined like this yet.
- One interesting thing is that the value for
animationsproperty can be eitherAnimationDriver<E>orundefined- This is done so with a conditional type (i.e. in the code, it is declared like this:
E extends AnimationConfig ? AnimationDriver<E> : undefined). - In others words, if
Ehas the shape of type:AnimationConfig, then the type should be:AnimationDriver<AnimationConfig>, otherwise it'll beundefined. - In the docs, it is mentioned that tamagui supports different "animation" API's (e.g. animated, moti, etc)., so this type could be related to that.
- The type
AnimationConfigis a record that accepts any value. - See #
AnimationDriverbullet point for more info onAnimationDriver.
- This is done so with a conditional type (i.e. in the code, it is declared like this:
- E.g.
InferTamaguiConfigaccepts a generic parameterConf, and is used in a conditional type, i.e.InferTamaguiConfig<Conf> = Conf extends ConfProps<...>; i.e. if the shape ofConfmatchesConfPropsthen the type will be:TamaguiInternalConfig<...>, otherwise it'sunknown.- As discussed in the prev. bullet point,
ConfPropsaccepts generic parameters "A through I". - Usage of type
ConfPropsinInferTamaguiConfig, prefixes the generic parameter declarations "A through I" w/ theinferkeyword (hence the nameInferTamaguiConfig). - Generic parameters "A through I" from
ConfPropsare then passed toTamaguiInternalConfig. - But what does the
inferkeyword do in this context? is it just to foce type inference? - So the return type
InferTamaguiConfig<CreateTamaguiProps>is really just:TamaguiInternalConfig?
- As discussed in the prev. bullet point,
- The
createTamaguifunction at first glance looks like a parser. - # There is a call to a function called
createVariableswhich receives the argumentconfigIn.tokenswhich has type of:tokens?: GenericTokens | undefined.- The 1st parameter
tokensfromcreateVariablesaccepts a type of:DeepTokenObject- The usage of "deep" in the type name seems to imply that the object is a recursive type, i.e. in the sense that the value for a property in
DeepTokenObjectcan be either itself, orstring | number. - As such, given that there are only 2 outcomes for the properties's value type, no matter how "deep" the object is (e.g.
foo,foo.bar,foo.bar.baz), the final nested object will always have a property value of type:string | number. - This kinda makes sense since when using tokens (e.g. in
styled) you pass a "keypath" string, e.g.$foo.bar.baz, andbazcan either be a string or number.
- The usage of "deep" in the type name seems to imply that the object is a recursive type, i.e. in the sense that the value for a property in
- This function returns an object of type:
DeepVariableObject<DeepTokenObject>.- As such this function essentially transforms (or possibly remaps)
DeepTokenObjecttoDeepVariableObject. - For each property in
DeepTokenObject, if it's value is of type:string | number, then it's aVariable<string | number>; otherwise if the value of theDeepTokenObjectproperty is an object that has the shape ofDeepTokenObject, then it's aDeepVariableObject<DeepTokenObject>. - Essentially like,
DeepTokenObject, the use of "deep" in the naming of the type denotes that it is a recursive object; with a a property either having a value ofVariable<string | number>, or itself. - As such, no matter how "deep" the object is, the final nested object will have property with a value type of
Variable<string | number>. - So we can conclude, that this functions transforms
DeepTokenObjecttoDeepVariableObject.
- As such this function essentially transforms (or possibly remaps)
- The other params are meant to be used by the recursive call to itself to pass more info, e.g.
parentPath, andisFont. - Looking at the code, there seems to be a cache/"look up table" to store the prev. results (i.e. for optimization).
- Interestingly all the code needed to map a particular "tokens" object to
Variableobject is:cache.set(res, true)+cache.has(tokens). - This implies that the tokens object is hashable somehow, and the "order" of properties (when hashed) is consistent across different object instances so long as it's "shape" and value is the same; i thought that JS doesn't guarantee the order of objects? Maybe it sort's them alphabetically? or uses some other mechanism.
- Interestingly all the code needed to map a particular "tokens" object to
- So this chunk of code is traversing through a
DeepVariableObjectobject (because it has a recursive call to itself here)- E.g. all roads lead to
Variable<string | number>, no matter how "deep" the object is. - # The check for whether the current property is of
Variableis via done via a function calledisVariable(val)here; it just checks if the value passed is of type object, and has a property declared calledisVar. - If the property value is already of type:
Variable, then it gets added tores, otherwisecreateVariableis called first.
- E.g. all roads lead to
- There is some code that prefixes the current key with
$(and assigns it to a variable namedkeyWithPrefix), and another that creates a key that is not prefixed with$(which is assigned to variablekey).- This suggests that current key string may or may not have a
$prefix, and this chunk of code "normalizes" things.
- This suggests that current key string may or may not have a
- Code - Checks that the current property value is of
Variable, then skips to next property. - Code - Some code related to the construction of a string value for variable called
name; it is in kebab case "key path" of sorts (e.g.foo-bar-baz), representing the current object path?- I say of sorts because the key seems to be hashed via
simpleHash(key)(which defined inpackages/simple-hash). - As such it's more likely like:
1-2-3-ahdh374or maybe even:jfwe32-efwse-2scwe. - I think this might be for the CSS name? Used to prevent class name collisions?
- I say of sorts because the key seems to be hashed via
- As mentioned earlier, there can be an invocation to
createVariableat the end.- Hence the name
createVariables, i.e.createVariables->createVariable. - The first parameter named props, accepts
VariableIn<string | number | Variable>(declared here). VariableInis a subset of objectVariable; only containing the properties:key: string,name: string,val: string | number | Variable.- This type is possibly recursive since
valcan be of typeVariable, andVariableInis a subset ofVariable. createVariablereturns aVariable<string | number | Variable>object.- The properties
keyand,valfrom the props param. is just copied over to the return object. - The property
Variable.isVaris explicitly set to true in the object to be returned; probably used "brand" an object; this property is set here. - The value for
Variable.namecould just be a copy ofprops.name, or passed throughsimpleHash(name, 40). - Finally, the
Variable.variableis a css variable string if on the web, otherwise it's just an empty string.
- The properties
- Hence the name
- From this,
createVariable+Variabletype is used to create and represent CSS variables in the JS side.
- The 1st parameter
- Let's head back to the
createTamaguifunction again; there seems to be logic that traverses throughtokens(i.e. the value from the output ofcreateVariables(configIn.tokens)).- Even though
tokensis of typeDeepVariableObject<GenericTokens>(with "deep" suggesting that it is of recursive type), the "for loop" used to traversetokensonly goes two levels deep; e.g. traversing from each category in the tokens object (which is aVariable), and then traversing through each item in the category object, and so on. - As such we have to assume that the value being extracted here is a value of:
Variable<string | number>? Type inference says that it'sany. - It seems that the leaf objects (i.e. the deepest object that is the shape of
Variable<string | number>, e.g. forfoo.bar.baz,bazis the leaf object) intokensare plucked and stored intokensParsed. - #
tokensMergedon the other hand is similar totokensParsed, but it contains duplicate properties (keys with and without the$prefix);tokensMergedis passedsetTokensand is probably cached as well.- The docs mentions that you can optionally obtain a token property key that is prefixed with or without
$viagetTokens({prefixed: boolean}). - As such, this could be the reason why
tokensMergedstores duplicate properties but i am not sure yet. - Continued in
getTokens().
- The docs mentions that you can optionally obtain a token property key that is prefixed with or without
- Even though
- Code - Parsing the fonts config which gets assigned to
fontsParsed; TBC. - # Code - Parsing the theme config, which gets assigned to
themeConfig; TBC. - Code - All of the parsed data is used to construct a
TamaguiInternalConfigobject namedconfig. - Code - The
configobject is then passed as an argument to other "setup-like" functions: i.e.configureMedia(config),setConfig(config),createdConfigs.set(config, true)- #
setConfig(next: TamaguiInternalConfig)is used to cache the tamgui config. For example it is used here to read the tokens. - TBC.
- #
- Code - There is an event emitter
configListenersthat notifies it's listeners that the config has been processed. - Code - Some debugging related-code; namely printing the config to the console, and creating a global
Tamaguiobject if needed. - Code -
configis then typed-erased, and returned so thatInferTamaguiConfig<CreateTamaguiProps>is the inferred return type.
- The
- Let's go back to the docs; In order to create a tamagui config the ff. are defined: a "font config" (created via
createFont({...})), a "token map" (created viacreateTokens), a "themes config", a "media queries" object (created viacreateMedia({...})), a "shorthands" substitution map.- Docs:
theme: Define your design theme, which map to CSS properties. - Docs:
media: Define reusable responsive media queries. - Docs:
shorthands: Define any props you want to expand to style values, keys being the shorthand and values being the expanded style prop.
- Docs:
- It all starts with creating a tamagui config:
-
Tokens:
- Docs: Use
createTokensto generate variables in your theme and app; They are mapped to CSS variables at build time. - Font Tokens:
- Clarification: "Tamagui core doesn’t do any matching but the way the higher level tamagui components are designed they try and use the properties when you use “size” for font "
- Docs: Created with
createFont; [Customize "styling" of font] based on each font family- E.g. custom defined
size/lineHeight/weightby family; choose a specific "vertical rhythm" per font. - Docs: Note, you don't need to use numbered keys, you can use
smortiny[so long as the keys are consistent].
- E.g. custom defined
- There seems to be a concept of a "main" key, in the sense that across font category tokens, they must follow the same sets of keys defined in
GenericFont.size.- Docs: The keys of
size,lineHeight,weight, andletterSpacingare meant to match; Define the full set of keys onsize, the rest can be a subset. - E.g. if the font "token key" =
$small, it will use the matching values in the font categories; thesizeof the text will be the value defined inGenericFont.size['$small'], and thelineHeightof the text will beGenericFont.lineHeight['$small'], and so on. - Docs: Missing keys from partially defined objects will be filled in; At creation Tamagui fills in the missing keys with previous value, or the next one if no previous exists.
- Docs: The keys of
- To use tokens, you pass a string prefixed with
$(e.g."$small") to a "style" prop (e.g.<Text fontSize="$large">,<Text fontFamily="$mono"/>). - Non-Font Tokens:
- These tokens categories are not specific to fonts, e.g.
space,size,color, etc.
- These tokens categories are not specific to fonts, e.g.
- Tokens are kind of like a static KV-store, and is used to remap keys to concrete constant values (value substitution map?) in RN, but uses CSS variables on the web.
- Clarification: "tokens never change. Defined only once".
- E.g.
tokens = {size: {none: 0, small: 3}}and<View radius="$small"/>, then<View radius={3}/>. - But also, since it is highly recommended that the "token keys" remain consistent across token categories (e.g.
tokens['size'] = {none: 0, small: 3},tokens['space'] = {none: 0, small: 4},tokens['radius'] = {none: 0, small: 1},tokens['zIndex'] = {none: 0, small: 1}, etc)., then it implies that all of these will be automatically substitute the values to the associated token value "all at once" given a token key. - E.g. if the token key is
$small, then:size={3},space={4},radius={1}, and so on; Essentially, it's kind of like pattern matching + value substitution. - I don't know yet what API allows to do these, but i think i remember reading about it in "groups" and "themes".
- The type explicitly declares the properties:
size,color,space,radius, andzIndex; so these particular categories are reserved, and have special meaning. - The docs mentioned this: "Tamagui knows which tokens to use based on the style property you use"; as such, the reserved token categories must be mapped to certain style props (e.g.
GenericToken.colormapping toView.styles.backgroundColor, or rather:View.backgroundColorbecause styles in in-lined as props). - As such, a token is a global static variable (that is JS representation of CSS variables), w/ special logic that allows for it to be used in the "tamagui config" itself (e.g. themes), but also in components style props (in which the token key is substituted with the concrete values, either via CSS variables or regular JS values at "compile" time?).
- As prev. explored in the
createTamaguifunction, tokens are created usingcreateTokens.createTokens({...})->createTamagui({tokens, ...}).const tokens = createTokens({size: { small: 10 }).- The
createTokensfunction accepts a parameter namedtokensthat is of type:CreateTokens, and returns an object of typeMakeTokens<CreateTokens>defined here. - # Let's start w/ the type
CreateTokens; it is a combination of{categoryKey: {tokenKey: Val}}+Record<'color' | 'space' | 'size' | 'radius', 'zIndex', Record<string, Val>>, whereValis just:number | string | Variable; so it's a map of maps.- The first part are the type def. for custom category tokens, while the 2nd part are special/reserved category tokens that have some logic that lets them be auto mapped to certain style props.
- So the "lossy" type for this is:
Record<string, Record<string, Val>>orRecord<string, Record<number | string | Variable>>.
- Next, let's take a look at
MakeTokens<CreateTokens>(which is defined here):- It has the ff. comment: "verbose but gives us nice types...", "removes
$prefix allowing for defining either as$1:or1:which is important because Chrome re-orders numerical-seeming keys :/". - The type is a bit complex, but from what i can gather (based on the comment), the type is declared this way for better TS inference + autocomplete.
- A type named
NormalizeTokensseems to be used to remove the$prefix.NormalizeTokensdeclares generic typeAwhich is the type of the object (but based on this context, this is likely a "token category" map, e.g.size: {...},color: {...}, etc or more succinctly:Record<string, VariableVal>).
- This type uses conditional types and asks the question
T extends {...}Tgeneric param. is defined asT extends CreateTokens(jump here for more info).
- It has the ff. comment: "verbose but gives us nice types...", "removes
- This function seems to call and pass the
tokensargument tocreateVariablesand returns the type-erased result so that the return type will beMakeTokens<CreateTokens>. - Jump here for more info about
createVariables, but basically this function returns an object of type:DeepVariableObject<DeepTokenObject> - This kinda makes sense since variables are created from tokens.
- "tokens -> variables", i.e. tokens is the config for making variables, and variables are the "processed" version of tokens.
- As such, under the hood, tokens are just variables (or maybe "tokens" is the public API, and "variables" is the internal representation).
- In either case, i think the tamgui compiler will take all the defined variables, and use it to generate CSS but i haven't found the code yet that does this.
- Docs: "Use
createTokensto generate variables in your theme and app; They are mapped to CSS variables at build time".
- This is also the reason why
getTokens(discussed here) returns an object of typeVariable.
- # Docs: "you can access your tokens from anywhere using
getTokens", e.g.getTokens().size.smallorgetTokens().size['$small'], and the value you receive will be of typeVariable.- #
getTokens()returns an object of typeTokensMerged, which is defined as:TokensParsed & Tokens.- Let's start w/
Tokenstypes, which is just an alias defined as:TamaguiConfig['tokens'] - Traverse Type:
TamaguiConfig->GenericTamaguiConfig->GenericTokens->CreateTokens. - So the
Tokenstype is really justCreateTokenstype; Jump here for more details. TokensParsedis similar toTokensbecause it has same keys, i.e. it remapsTokenstoTokenPrefixed<Tokens[KeyInTokens]>); this type just adds a$to the property key?- As such, the reason why this type is called
TokensMergedbecause it contains the tokens + a copy that are prefixed with$(e.g.$foo.barandfoo.bar); this type is used is used here.
- Let's start w/
- # The tokens are retrieved by reading a file-scoped variable called
conf, w/ it's type being defined as:TamaguiInternalConfig | null.- The
confvariable looks like it contains the cached config returned bycreateTamagui(which is set here).
- The
- The docs mention helpers functions: "
getVariablewhich will return the CSS variable on web, but raw value on native, andgetVariableValuewhich always returns the raw value". - It looks like
Variableis the JS representation of "CSS variables".
- #
- Subtopic: "Using tokens with components"
- Docs: "Tokens are automatically applied to certain properties. For example,
sizetokens are applied to width and height. And of courseradiusto borderRadius". - Tokens are available in built-in tamagui components (e.g.
Stack), as well as in components wrapped usingstyled.
- Docs: "Tokens are automatically applied to certain properties. For example,
- Subtopic: Specific tokens
- Specific tokens (i.e. custom user-defined tokens) can be created using
createTokens, e.g.createTokens({customToken: {tokenKey: 12}}). - It can then be accessed via the "specific tokens" syntax (which involves the explicit declaration of the category name), e.g.
<Stack height={$customToken.tokenKey}.
- Specific tokens (i.e. custom user-defined tokens) can be created using
- Docs: Use
-
Themes:
- Clarification: "Themes = css variables but you can change them contextually for other".
- Docs - Note: Tokens are considered a "fallback" to themes, so any values you define in your theme will override the token.
- Docs: "Color tokens as fallback values for themes" (i.e.
tokens.color, the color token category). - Docs: "Color tokens are available as fallback values when you access a theme. So when you
useTheme()and then access a value that isn't in the theme, it will check for atokens.colorwith the matching name".
- Docs: "Color tokens as fallback values for themes" (i.e.
- Tokens are variables, themes use tokens to "to create consistent, generic properties that you then typically use in shareable components"; however, themes should generally only deal with colors.
-
Styles: "The Tamagui superset of React Native styles"
- Docs: Similar to "styled components", you can use
styled()to create components that contain styles (e.g. derivatives ofViewandText). - In tamagui, styles are individual props you can pass to the component (e.g.
<View padding={10}/>). - # It seems that
StackStyleBaseis the root/base type for styles. StackStyleBasetype is an interface that is a union/combination of several objects:ViewStyle,TransformStyleProps,ExtraStyleProps, andOverrideNonStyledProps.ViewStyleis imported from RN; although some properties are excluded viaOmit(i.e.keyof OverrideNonStyledProps, andelevation).TransformStylePropsis an object that contains 3d transform-related properties (e.g.x,y,rotateX,rotateY).ExtraStylePropsis a pretty big object; a lot of the properties are marked web-only.- Some of the properties accept tokens: e.g.
borderInlineColor?: ColorTokens,borderBlockWidth?: SpaceTokens | number,blockSize?: SizeTokens | number. - I think i saw this type somewhere in the docs (update: it's mentioned here).
- A lot of the properties seems to be a remapped from an object type called
Properties(e.g.contain?: Properties['contain']), and marked as web-only. - Ah,
Propertiesis imported fromcsstype. Makes sense why they are all marked as web-only. - There are some animation related properties, e.g.:
animation?: AnimationProp | null,animateOnly?: string[],animatePresence?: boolean. - There are some properties that are remapped from RN
ViewStyle, e.g.:borderBlockStyle?: ViewStyle['borderStyle'],borderBlockStartStyle?: ViewStyle['borderStyle'], etc. - These seem to be the styles that are in-lined as props?
- Some of the properties accept tokens: e.g.
- Subtopic - Parent Based Styling:
- Docs: Tamagui has a variety of ways to style a child based on the "parent", a parent being one of: platform (e.g.
$platform-ios), screen size (i.e. via user-defined media queries, e.g.$large), theme (e.g.$theme-light), or group (e.g.$group-header). - Subtopic - Parent Based Styling: Media Query
- Subtopic - Parent Based Styling: Theme
- Subtopic - Parent Based Styling: Platform
- Subtopic - Parent Based Styling: Group
- Subtopic - Parent Based Styling: Group Container
- Docs: Tamagui has a variety of ways to style a child based on the "parent", a parent being one of: platform (e.g.
- Docs: Similar to "styled components", you can use
-
Media:
-
Animations:
- Topic:
createMedia- The
createMediafunction (defined in package"@tamagui/react-native-media-driver") defines a parameter namedmediawhich is a map defined as:Record<string, MediaQueryObject>, and also returns object of type:Record<string, MediaQueryObject>.- This is because it just returns the
mediaparameter.
- This is because it just returns the
- Inside the body of the function, it invokes
setupMatchMedia(matchMedia). - The function
setupMatchMediauses platform specific code and is implemented in two files:helpers/matchMedia.ts(web), andhelpers/matchMedia.native.ts(native),- On the web (
matchMedia.ts), thesetupMatchMediafunction is no-op, and has the ff. comment: "no-op web", - On native (i.e.
matchMedia.native.ts), thesetupMatchMediaassigns the argument you pass to it (i.e.MatchMedia) toglobalThis['matchMedia'](the global context); effectively providing a shim for the web'smatchMediaon native. - On native i.e. (
helpers/matchMedia.native.ts), there is file level variable calledmatchMediaImpl.- The
matchMediaImplvariable has a type of:MatchMedia; as such, this variable contains the native impl. ofmatchMedia. - The default value for
matchMediaImplismatchMediaFallback. - It looks like
matchMediaFallbackis a dummy/placeholder implementation. - On the dev build, invoking
matchMediaFallbackwill result in a warning: "warning: matchMedia implementation is not provided". - The return value of
matchMediaFallback(i.e.MediaQueryList) contains no-op implementations. - As the actual impl. will be provided later; this is because the file exports a setter function for
matchMediaImplnamed:setupMatchMedia.
- The
- On the web (
- Now let's discuss the
matchMediaargument passed tosetupMatchMedia(i.e.setupMatchMedia(matchMedia)).- The argument
matchMediais a function of type:MatchMedia.- The
MatchMediatype is defined as:(query: string) => MediaQueryList. MediaQueryListis an interface to an object that has functions (i.e.addListener,removeListener,match), and a property calledmatches.- This resembles the return value of the function
window.matchMedia(mediaQueryString). - The class
NativeMediaQueryListimplements this. - Based on this, we can conclude that
MatchMediaandMediaQueryListtypes is a mirror/shim of thematchMediaweb API and it serves a proxy/bridge to have a common type definition between native and JS.
- The
- The
matchMediafunction uses platform specific code and is implemented in two files:matchMedia.ts(web), andmatchMedia.native.ts(native). - On the web (i.e.
matchMedia.ts), the values assigned to thematchMediaconstant is:globalThis['matchMedia'],- This function is defined like this:
function matchMedia(query: string): MediaQueryList;and is part of the browser/web api (mdn ref). - So it's an alias to the web's
matchMediaAPI.
- This function is defined like this:
- On native (i.e,
matchMedia.native.ts),matchMediais a function that instantiates aNativeMediaQueryListobject, and passesqueryargument to it, then returns the instantiated object.- The
NativeMediaQueryListclass implementsMediaQueryList, and implements all the needed methods + properties to conform toMediaQueryList, i.e.addListener,removeListener,match, and a property calledmatches. - The constructor for
NativeMediaQueryListacceptsquery: string. - This class seems to use
Dimensionsmodule to implement the media query api. - The
Listenersare defined as:(orientation: Orientation) => void. - There are calls to a function named
matchQuery(i.e. in methodmatchQuery, and gettermatches).
- The
- The argument
- In conclusion, the
matchMediafunction passed tosetupMatchMediais the platform's impl. of the "match media" API, andsetupMatchMediamakes sure thatmatchMediais available in the global context.
- The
- # Topic -Type:
AnimationDriver-
The type
AnimationDriverobject accepts a generic parameter namedAof typeAnimationConfig(see prev. bullet point). -
The generic param
Ais assigned as a value to a property named:animations, so this means this property accepts anAnimationConfigobject. -
There are two properties that are untyped:
View, andText(they are capitalized for some reason, but idk what is the intended meaning behind that; this could maybe mean that it accepts a react component?). -
This object type declares "feature" flag properties:
isReactNative, andsupportsCSSVars. Likely used for some logic related to the animation driver (e.g. deciding what "animation driver" to use for a particular platform). -
It has several properties that accepts functions, some of which are prefixed with
use(which might denote that they accept hook functions). -
The property
useAnimations, accepts a type calledUseAnimationHook.- This type is a function that takes a prop object as its only argument, and returns either
nullor an object that has a single optional property called style. - It is defined like so:
style?: StackStyleBase | StackStyleBase[], which is defined here; Jump to this section for more details. - At first glance, it kinda looks like the styles prop from RN because it can optionally accept an array.
- Also, in tamagui there are stack components, so they could be related to this type.
- This type is a function that takes a prop object as its only argument, and returns either
-
- Terminology:
styled,unstyled,headless.
Let's start with Checkbox
- The headless version of checkbox uses a pressable component as the "base/root" component.
- Then it explicitly declares a
checkedstate. - The
HeadlessCheckboxusesforwardRefto forward thePressablecomponents ref. - Then it uses a hook called
useCheckbox- The first parameter (props), accepts an argument of type
CheckboxProps.- It basically accepts a union comprising of:
ViewProps,PressableProps['onPress'], and a type calledCheckboxExtraProps. - It's usage in
HeadlessCheckbox, is that theuseCheckboxhook receivesHeadlessCheckbox.props. - The actual
HeadlessCheckboxdoesn't have any logic in it, mostly just UI stuff.
- It basically accepts a union comprising of:
- The second parameter is an array that de-structured to
checked, andsetCheckedtuple. The type ofcheckedisCheckedState, and the type ofsetCheckedisReact.Dispatch<React.SetStateAction<CheckedState>>.- What is
React.Dispatch? The type signature for it is:type Dispatch<T> = (action: T) => void;Dispatchtakes an action as a parameter and returns nothing meaningful (void). There are multiple types of actions (e.g.SetStateAction).
- As such,
React.SetStateAction<T>is an "action type" that mirrorssetState, and is to be used withReact.Dispatch<T>.- Note: The type for
useStateis:UseState<S> = (action: S | ((prevState: S) => S)) => void;.
- Note: The type for
- So in other words,
React.DispatchandSetStateActionis a way to describesetStatepurely in types.- So if we have the ff. code:
const [num, setNum] = useState(0), the type ofsetNumsetter will be:Dispatch<SetStateAction<number>>.
- So if we have the ff. code:
- In usage: the
checked, andsetCheckedtuple receives the current state "checked", and its corresponding setter. *
- What is
- The first parameter (props), accepts an argument of type
- Then it explicitly declares a