Context 的使用方法
提供了一个传递数据的方法,避免一层层的传递props带来的麻烦。
Context = React.createContext(defaultValue);
Context.Provider 包裹父组件,用value属性标明你要传递的值。
Context.Consumer 包裹需要接受props的子类元素,以匿名函数的形式接受value。
// Context使用方法
import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
// 需要自上而下传递的上下文对象
interface ContextValue {
color: string,
changeColor: (color: string) => void;
}
// 创建一个上下文的容器(组件)
const ColorContext = React.createContext<ContextValue | null>(null) // 或者修改配置 将 “strictNullChecks” 设置为false ,null就可以默认赋值给任何值。
// ColorContext = {Provider,Consumer} Provider提供者;Consumer接收者
// let {Provider,Consumer} = ColorContext //Provider 通过value,向下传递属性
class Header extends React.Component {
render() {
return (
// 谁要接收,就包裹谁
<div>
Header
<Title />
</div>
)
}
}
class Title extends React.Component {
render() {
return (
<ColorContext.Consumer>
{
(value: ContextValue | null) => (
<div style={{ border: `5px solid ${value!.color}` }}>
Title
</div>
)
}
</ColorContext.Consumer>
)
}
}
// ----
class Main extends React.Component {
render() {
return (
<div>
Main
<Content />
</div>
)
}
}
class Content extends React.Component {
render() {
return (
<ColorContext.Consumer>
{
(value: ContextValue | null) => (
<div style={{ border: `5px solid ${value!.color}` }}>
Content
<button onClick={() => value!.changeColor('red')}>变红</button>
<button onClick={() => value!.changeColor('green')}>变绿</button>
</div>
)
}
</ColorContext.Consumer>
)
}
}
// ----
interface PanelProps { }
interface PanelState {
color: string
}
class Panel extends React.Component<PanelProps, PanelState> {
constructor(props: PanelProps) {
super(props);
this.state = {
color: 'red'
}
}
changeColor = (color: string) => {
this.setState({ color })
}
render() {
let contextValue: ContextValue = { color: this.state.color, changeColor: this.changeColor }
return (
// 向下发送
<ColorContext.Provider value={contextValue}>
<div style={{ border: `5px solid ${this.state.color}` ,padding: 5}}>
Panel
<Header />
<Main />
</div>
</ColorContext.Provider>
)
}
}
ReactDOM.render(<Panel />, root)
Context 的实现原理
创建createContext函数,返回值为包含两个组件(Provider ,Consumer )的对象 。
- Provider : Provider 类返回它所包裹的元素 this.props.children,并且将收到的props记录到自己的静态属性上,便于向Consumer 传递。利用getDerivedStateFromProps生命周期函数,实现跟新state,重新刷新页面。
- Consumer: Consumer类,只需要执行传递的子类,传递参数(Provider.value)即可(因为Consumer包含的就是个返回子类元素的函数)。
import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
// 需要自上而下传递的上下文对象
interface ContextValue {
color: string,
changeColor: (color: string) => void;
}
interface ContextProps<T> {
value: T
}
function createContext<T>(defaultValue: T){
interface State {
value: T
}
class Provider extends React.Component<ContextProps<T>,State>{
static value: T = defaultValue; // 要让Consumer也可以拿到传递的值
constructor(props: ContextProps<T>){
super(props);
Provider.value = props.value;
this.state = {value: props.value}
}
// 当组件接收到新的属性对象时。可以基于老的状态对象,和新的属性对象得到新的状态对象
static getDerivedStateFromProps(nextProps: ContextProps<T>,prevState: State){
Provider.value = nextProps.value;
return {...prevState,value: nextProps.value} // 返回新的状态,刷新页面(因为里面的值没有用,给个空对象也可以正常运行)
}
render(){
return this.props.children;
}
}
interface ConsummerProps {
children: (value: T) => React.ReactNode
}
class Consumer extends React.Component<ConsummerProps> {
render(){
return this.props.children(Provider.value)
}
}
return {
Provider,
Consumer
}
}
const ColorContext = createContext<ContextValue | null>(null) // 或者修改配置 将 “strictNullChecks” 设置为false null就可以默认赋值给任何值。
// ColorContext = {Provider,Consumer}
// let {Provider,Consumer} = ColorContext //Provider 通过value,向下传递属性
class Header extends React.Component {
// 类组件中 直接声明 static contextType = ColorContext后,调用 this.context.xxx 就可以直接使用。
render() {
return (
// 谁要接收,就包裹谁
<div>
Header
<Title />
</div>
)
}
}
class Title extends React.Component {
render() {
return (
<ColorContext.Consumer>
{
(value: ContextValue | null) => (
<div style={{ border: `5px solid ${value!.color}` }}>
Title
</div>
)
}
</ColorContext.Consumer>
)
}
}
// ----
class Main extends React.Component {
render() {
return (
<div>
Main
<Content />
</div>
)
}
}
class Content extends React.Component {
render() {
return (
<ColorContext.Consumer>
{
(value: ContextValue | null) => (
<div style={{ border: `5px solid ${value!.color}` }}>
Content
<button onClick={() => value!.changeColor('red')}>变红</button>
<button onClick={() => value!.changeColor('green')}>变绿</button>
</div>
)
}
</ColorContext.Consumer>
)
}
}
// ----
interface PanelProps { }
interface PanelState {
color: string
}
class Panel extends React.Component<PanelProps, PanelState> {
constructor(props: PanelProps) {
super(props);
this.state = {
color: 'red'
}
}
changeColor = (color: string) => {
this.setState({ color })
}
render() {
let contextValue: ContextValue = { color: this.state.color, changeColor: this.changeColor }
return (
// 向下发送
<ColorContext.Provider value={contextValue}>
<div style={{ border: `5px solid ${this.state.color}` ,padding: 5}}>
Panel
<Header />
<Main />
</div>
</ColorContext.Provider>
)
}
}
ReactDOM.render(<Panel />, root)
高阶组件的嵌套
函数包裹,返回新的类组件,给组件传递值(props)可以理解为(组件的扩展);
重在 值之间的相互传递。
// 高阶组件的嵌套 1
import React, { ReactText } from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
interface Props {
render: (value: State) => React.ReactNode
}
interface State {
x: number,
y: number
}
type PropsFromState = State;
class MouseTracker extends React.Component<Props,State> {
constructor(props: Props){
super(props);
this.state = {
x: 0,
y: 0
}
}
handerMouseMove = (ev: React.MouseEvent) => {
this.setState({
x: ev.clientX,
y: ev.clientY
})
}
render(){
return (
<div onMouseMove={this.handerMouseMove} style={{border: '1px solid red'}}>
{this.props.render(this.state)}
</div>
)
}
}
const MyComponent = (value: PropsFromState) => (
<div>
<p>位置</p>
<p>当前位置x: {value.x}; y: {value.y}</p>
</div>
)
function withMouseTracker(OldComponent: React.FC<PropsFromState>){
return (props: {}) => {
return (
<MouseTracker render={
mouseProps => <OldComponent {...props} {...mouseProps} />
}/>
)
}
}
const WithMouseTrackerComponent = withMouseTracker(MyComponent);
ReactDOM.render( <WithMouseTrackerComponent />,root)
// 高阶组件的嵌套 2
// localStorage.setItem('username','uzi')
// name.json => {"uzi":"义勇"} (json文件一定要与index.html同级)
// 函数式组件类型(React.ReactFC<Props>)和类组件类型(React.ComponentClass<Props>)
import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');
// 传的时候是由里往外传,渲染的时候,是由外往内渲染。
interface ComponentProps {
value: string;
};
interface State {
value: string
}
// React.FC/FunctionComponent 定义函数组件
const fromLocal = (OldComponent: React.FC<ComponentProps> | React.ComponentClass<ComponentProps>) => {
return class extends React.Component<ComponentProps,State> {
state: State = {value: ''}
componentWillMount(){
let value = localStorage.getItem(this.props.value)
if(value){
this.setState({
value: value
})
}
}
render(){
return <OldComponent value={this.state.value} />
}
}
}
const fromAjax = (OldComponent: React.FC<ComponentProps>) => {
return class extends React.Component<ComponentProps,State> {
state: State = {value: ''}
componentDidMount(){
fetch('/name.json').then( response => response.json() ).then( result => {
this.setState({
value: result[this.props.value]
})
})
}
render(){
return <OldComponent value={this.state.value}/>
}
}
}
// 希望这个组件可以localStorage中获取到username这个字段的值。
const Username = (props: ComponentProps) => {
return <input defaultValue = {props.value} />
}
const FromAjax = fromAjax(Username);
const FromLocalUsername = fromLocal(FromAjax);
ReactDOM.render(<FromLocalUsername value='username'/>, root)