(Day2) React + TypeScript

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 )的对象 。

  1. Provider : Provider 类返回它所包裹的元素 this.props.children,并且将收到的props记录到自己的静态属性上,便于向Consumer 传递。利用getDerivedStateFromProps生命周期函数,实现跟新state,重新刷新页面。
  2. 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)

猜你喜欢

转载自blog.csdn.net/qq_42387542/article/details/107504743
今日推荐