新的 React 上下文 API 和流错误,尝试渲染消费者时



当我尝试渲染消费者时,流显示下一个错误:

[flow] 无法创建SidebarContextConsumer元素,因为在属性children的第一个参数中未定义的 [1] 中缺少属性changeOpenState。(参考资料: [1](

这是我的代码:

// @flow
import React, { createContext } from 'react';
import type { Context, ProviderProps } from './Sidebar.types';
const SidebarContext = createContext();
export const SidebarContextConsumer = SidebarContext.Consumer;
/* eslint-disable react/no-unused-state */
export class SidebarContextProvider extends React.Component<ProviderProps, Context> {
state = {
dynamic: false,
open: false,
transition: false,
changeDynamicMode: (dynamic: boolean) => {
this.setState({
dynamic,
open: false,
transition: false,
});
},
changeOpenState: (open: boolean, transition: boolean = true) => {
this.setState({ open, transition });
},
};
render() {
const { children } = this.props;
return (
<SidebarContext.Provider value={this.state}>
{children}
</SidebarContext.Provider>
);
}
}
/* eslint-enable */

流声明:

export type Context = {
changeDynamicMode: (dynamic: boolean) => void,
changeOpenState: (open: boolean, transition?: boolean) => void,
dynamic: boolean,
open: boolean,
transition: boolean,
};

这似乎是流类型的已知限制。

我通过以下方式解决了错误:

const SidebarContext: Object = createContext();

TL;DR 这可以通过向createContext添加默认值来解决

我认为问题在于 Flow 在如何与React.createContext()交互方面存在逻辑缺陷。首先,我们可以使用React.Context<myObject>指定上下文值。不幸的是,如果我们不指定默认参数,Flow会正确实现myObject可以undefined

一个简单的测试用例,你可以在 Try Flow 中试用:

从"反应"导入 * 作为反应;

type obj = {| a: string, b: number |}
const ctxt: React.Context<?obj> = React.createContext();
const cmpnt = () => (
<ctxt.Provider value={{ a: "a", b: 2 }}>
<ctxt.Consumer>
{({a}) => console.log(a.toUpperCase())}
</ctxt.Consumer>
</ctxt.Provider>);

正如您在 Flow 0.78 中看到的错误:

{({a}) => console.log(a.toUpperCase())}
^ Cannot create `ctxt.Consumer` element because property `a` is missing in null or undefined [1] in the first argument of property `children`.
References:
4: const ctxt: React.Context<?obj> = React.createContext();

如果我们添加一个default它就会消失:

import * as React from 'react';
type obj = {| a: string, b: number |}
const ctxt: React.Context<obj> = React.createContext({ a: "test", b: 2 });
const cmpnt = () => (
<ctxt.Provider value={{ a: "a", b: 2 }}>
<ctxt.Consumer>
{({a}) => console.log(a.toUpperCase())}
</ctxt.Consumer>
</ctxt.Provider>);

针对原始问题的建议修复

import * as React from 'react';
type Context = {
changeDynamicMode: (dynamic: boolean) => void,
changeOpenState: (open: boolean, transition?: boolean) => void,
dynamic: boolean,
open: boolean,
transition: boolean,
};
const defaultState: Context = {
dynamic: false,
open: false,
transition: false,
changeDynamicMode: (dynamic: boolean) => console.error('Fix function!', dynamic),
changeOpenState: (open: boolean, transition: boolean = true) => console.error('Fix function!', open, transition),
}
const ctxt = React.createContext(defaultState);
class SidebarContextProvider extends React.Component<mixed & { children: React.Node }, Context> {
state = {
dynamic: false,
open: false,
transition: false,
changeDynamicMode: (dynamic: boolean) => {
this.setState({
dynamic,
open: false,
transition: false,
});
},
changeOpenState: (open: boolean, transition: boolean = true) => {
this.setState({ open, transition });
},
};
render() {
const { children } = this.props;
return (
<ctxt.Provider value={this.state}>
{children}
</ctxt.Provider>
);
}
}

以下内容对我有用

export const SidebarContextConsumer = ((SidebarContext.Consumer: any):
React$ComponentType<{
children: (value: Context) => ?React$Node
}>
);

我已经投射了任何并恢复了原始的消费者类型def,但可能删除了值类型

React$ComponentType<{ children: (value: ?Context) => ?React$Node}>

对于 flow,这就是我通常键入上下文的方式(没有初始值(:

const SidebarContext = createContext<Context | typeof undefined>();

其中Context是自定义type

同样有用的是为您的Context使用确切的类型,因此如果有人尝试使用未在Context type中定义的属性/键,流将引发错误。

最新更新