Данная глава посвящена рассмотрению влияния типизации на такой механизм как контекст, который хотя и не привносит ничего, что на текущий момент могло бы вызвать удивление, все же имеет один не очевидный момент.
Определение контекста осуществляется при помощи универсальной функции определяющей один обязательный параметр выступающий в качестве инициализационного значения и возвращающей объект контекста createContext<T>(initialValue: T): Context<T>. В случаях когда инициализационное значение в полной мере соответствует предполагаемому типу, чьё описание не включает необязательных членов, то аргументы типа можно или даже нужно не указывать. В остальных случаях это становится необходимостью.
import{createContext}from'react';/**Аргумента типа не требуется */interfaceAContext{a:number;b:string;}/**[0] [1][2] */exportconstA=createContext({a:0,b:``,});/** * Поскольку при определении контекста [0] * в качестве обязательного аргумента было * установлено значение [2] полностью соответствующее * предполагаемому типу AContext, аргумент типа * универсальной функции можно опустить [1]. * *//**Требуется аргумент типа */interfaceBContextextendsAContext{c?:boolean;}/**[0] [1] */exportconstB=createContext<BContext>({a:0,b:``,});/** * Так как инициализационное значение [1] * лишь частично соответствует предполагаемому * типу BContext тип объякта контекста необходимо * конкретизировать при помощью аргументов типа [0] *//**Требуется аргумент типа *//**[0] [1] */exportconstC=createContext<BContext|null>(null);/** * По причине отсутствия на момент определения * инициализационного значения оно замененно на null [1], * что требует упомянуть при конкретизации типа значения [0]. */
Поскольку функциональные и классовые компоненты подразумевают различные подходы для взаимодействия с одними механизмами реализуемыми React, после регистрации контекста в react дереве работа с ним зависит от вида компонента нуждающегося в поставляемых им данных.
После определения контекста необходимо зарегистрировать в react дереве предоставляемого им Provider установив ему необходимые данные.
Поскольку компонент является классовым, единственный способ добраться до предоставляемых контекстом данных заключается в создании экземпляра Consumer, который в качестве children ожидает функцию обозначаемую как render callback. Данная функция определяет единственный параметр принадлежащий к типу данных передаваемых с помощью контекста, а возвращаемое ею значение должно принадлежать к любому допустимому типу представляющему элемент React дерева.
1 2 3 4 5 6 7 8 9101112131415161718192021
classClassComponentextendsComponent{render(){return(/**[0] [1] [2] */<Context.Consumer>{(data)=><span>{data.message}</span>}</Context.Consumer>);}}/** * Поскольку компонент ClassComponent явялется * классовым, единственный вариант получить в нем * данные предоставляемые контекстом заключается * в создании экземпляра Consumer, который в качестве * children ожидает функцию обозначаемую как render callback * единственный параметр которой принадлежит к типу данных, а * возвращаемое значение должно принадлежать к одному из допустимых * типов предполагаемых React. */
Случаи предполагающие определение render callback вне Consumer потребуют указания аннотации типа его единственному параметру, тип для которого лучше объявить в месте определения контекста. Если инициализационные данные в полной мере соответствуют ожидаемому типу, то его получение проще выполнить с помощью механизма запроса типа, чем описывать вручную. В остальных случаях описание потребуется выполнять самостоятельно.
1 2 3 4 5 6 7 8 9101112131415
/**0 */letinitialValue={status:``,message:``,};constContext=createContext(initialValue);/**[1] [2] */typeContextType=typeofinitialValue;/** * [0] определение инициализационного значения, * на основе которого при помощи запроса типа [2] * будет получен его тип [1]. */
Полученный тип необходимо будет указать в аннотации единственного параметра render callback при его определении.
1 2 3 4 5 6 7 8 9101112131415161718192021
classClassComponentextendsComponent{/**[0] [1] */renderCallback=(data:ContextType)=>(<span>{data.message}</span>);render(){return(/**[2] */<Context.Consumer>{this.renderCallback}</Context.Consumer>);}}/** * При внешнем [2] определении render callback как поля класса [0] * в аннотации тип его единственного параметра указан тип данных [1] * предоставляемых контекстом. * */
Если данные предоставляемые контекстом принадлежать к более общему типу, то параметр render callback можно конкретизировать.
/**[0] */interfaceMessage{message:string;}/**[0] */interfaceStatus{status:string;}/**[1] [2] */typeContextType=Message&Status;letinitialValue={status:``,message:``,};constContext=createContext(initialValue);/** * [0] объявление конкретных типов * определяющих тип пересечение [2] * на который ссылается преждний псевдоним [1]. * * Поскольку инициализационное значение в полной * мере соответствует предполагаемому типу, переменную * initialValue и универсальную функцию можно избавить от * явной и излишней конкретизации. */classClassComponentextendsComponent{/**[3] */renderCallback=(data:Message)=>(<span>{data.message}</span>);render(){return(<Context.Consumer>{this.renderCallback}</Context.Consumer>);}}/** * [3] параметр render callback теперь ограничен типом * Message. */
Для получения данных распространяемых контекстом внутри тела функционального компонента, помимо варианта с Consumer, который ничем не отличается от рассмотренного в этой теме ранее, предусмотрен более предпочтительный способ предполагающий использование предопределенного хука useContext<T>(context).
Универсальная функция useContext ожидает в качестве своего единственного аргумента объект контекста, конкретизировать который с помощью аргумента типа не имеет никакого смысла.
При попытке с помощью аргумента типа ограничить более общий тип данных возникнет ошибка, поскольку в действие включаются правила контрвариантности параметров функции.
1 2 3 4 5 6 7 8 91011
constFunctionComponent=()=>{/**[0] [1] */let{message}=useContext<Message>(Context);// Errorreturn<span>{message}</span>;};/** * При попке ограничить тип с помощью аргумента типа [0] * из-за контрвариантности параметров функции возникнет ошибка [1]. */