likes
comments
collection
share

【前端丛林】React这样服用,效果更佳(4)

作者站长头像
站长
· 阅读数 45

前言

哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是国庆假期第四天,也是我们冒险的第四天,昨天我们介绍了React的生命周期、条件渲染和状态提升,今天我们会继续深入学习React高级用法。在这七天假期里,让我们学会React实现弯道超车。你们准备好了吗?那么开始我们的冒险之旅吧!

1.Context(上下文)

  • 在某些场景下,你想在整个组件树中传递数据,但却不想手动地在每一层传递属性。你可以直接在 React 中使用强大的 context API解决上述问题
  • 在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props

【前端丛林】React这样服用,效果更佳(4)

1.1 类组件使用

import React from 'react';
import ReactDOM from 'react-dom';
let ThemeContext = React.createContext();
class Title extends React.Component {
    static contextType = ThemeContext
    render() {
        return (
            <div style={{ border: `5px solid ${this.context.color}` }}>
                Title
            </div>
        )
    }
}
class Header extends React.Component {
    static contextType = ThemeContext
    render() {
        return (
            <div style={{ border: `5px solid ${this.context.color}` }}>
                Header
                <Title />
            </div>
        )
    }
}
class Content extends React.Component {
    static contextType = ThemeContext
    render() {
        return (
            <div style={{ border: `5px solid ${this.context.color}` }}>
                Content
                <button onClick={() => this.context.changeColor('red')}>变红</button>
                <button onClick={() => this.context.changeColor('green')}>变绿</button>
            </div>
        )
    }
}
class Main extends React.Component {
    static contextType = ThemeContext
    render() {
        return (
            <div style={{ border: `5px solid ${this.context.color}` }}>
                Main
                <Content />
            </div>
        )
    }
}
class Panel extends React.Component {
    state = { color: 'green' }
    changeColor = (color) => {
        this.setState({ color });
    }
    render() {
        let value = { color: this.state.color, changeColor: this.changeColor };
        //Provider提供者,它负责向下层所有的组件提供数据value
        return (
            <ThemeContext.Provider value={value}>
                <div style={{ border: `5px solid ${this.state.color}`, width: 300 }}>
                    Panel
                    <Header />
                    <Main />
                </div>
            </ThemeContext.Provider>
        )
    }
}
ReactDOM.render(<Panel />, document.getElementById('root'));

1.2 函数组件使用

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
let ThemeContext = React.createContext('theme');
class Header extends Component {
    render() {
        return (
            <ThemeContext.Consumer>
                {
                    value => (
                        <div style={{ border: `5px solid ${value.color}`, padding: 5 }}>
                            header
                            <Title />
                        </div>
                    )
                }
            </ThemeContext.Consumer>
        )
    }
}
class Title extends Component {
    static contextType = ThemeContext;
    render() {
        return (
            <ThemeContext.Consumer>
                {
                    value => (
                        <div style={{ border: `5px solid ${value.color}` }}>
                            title
                        </div>
                    )
                }
            </ThemeContext.Consumer>
        )
    }
}
class Main extends Component {
    static contextType = ThemeContext;
    render() {
        return (
            <ThemeContext.Consumer>
                {
                    value => (
                        <div style={{ border: `5px solid ${value.color}`, margin: 5, padding: 5 }}>
                            main
                            <Content />
                        </div>
                    )
                }
            </ThemeContext.Consumer>
        )
    }
}
class Content extends Component {
    static contextType = ThemeContext;
    render() {
        return (
            <ThemeContext.Consumer>
                {
                    value => (
                        <div style={{ border: `5px solid ${value.color}`, padding: 5 }}>
                            Content
                            <button onClick={() => value.changeColor('red')} style={{ color: 'red' }}>红色</button>
                            <button onClick={() => value.changeColor('green')} style={{ color: 'green' }}>绿色</button>
                        </div>
                    )
                }
            </ThemeContext.Consumer>
        )
    }
}
class Page extends Component {
    constructor() {
        super();
        this.state = { color: 'red' };
    }
    changeColor = (color) => {
        this.setState({ color })
    }
    render() {
        let contextVal = { changeColor: this.changeColor, color: this.state.color };
        return (
            <ThemeContext.Provider value={contextVal}>
                <div style={{ margin: '10px', border: `5px solid ${this.state.color}`, padding: 5, width: 200 }}>
                    page
                    <Header />
                    <Main />
                </div>
            </ThemeContext.Provider>
        )
    }
}
ReactDOM.render(<Page />, document.querySelector('#root'));

1.3 函数组件实现

function createContext() {
    let value;
    class Provider extends React.Component {
        constructor(props) {
            super(props);
            value = props.value
            this.state = {};
        }
        static getDerivedStateFromProps(nextProps, prevState) {
            value = nextProps.value;
            return {};
        }
        render() {
            return this.props.children;
        }
    }
    class Consumer extends React.Component {
        constructor(props) {
            super(props);
        }
        render() {
            return this.props.children(value);
        }
    }
    return {
        Provider,
        Consumer
    }
}
let ThemeContext = createContext('theme');

2. 高阶组件

  • 高阶组件就是一个函数,传给它一个组件,它返回一个新的组件
  • 高阶组件的作用其实就是为了组件之间的代码复用
const NewComponent = higherOrderComponent(OldComponent)

2.1 日志组件

import hoistNonReactStatics from 'hoist-non-react-statics';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
const logger = (WrappedComponent: React.FC) => {
    class LoggerComponent extends Component {
        start: number | null = null;
        componentWillMount() {
            this.start = Date.now();
        }
        componentDidMount() {
            console.log((Date.now() - this.start!) + 'ms')
        }
        render() {
            return <WrappedComponent />
        }
    }
    hoistNonReactStatics(LoggerComponent, WrappedComponent);
    return LoggerComponent;
}
let Hello = logger((props) => <h1>hello</h1>);
ReactDOM.render(<Hello />, document.getElementById('root'));

2.2 多层高阶组件

2.2.1 从localStorage中加载

  • localStorage 中有 username=zhansan
  • 从 localStorage 中根据key加载对应的值
localStorage.setItem('username','zhangsan');
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
interface WrappedComponentProps {
    value: string;
}
const fromLocal = (WrappedComponent: React.FC<WrappedComponentProps> | React.ComponentCl
ass<WrappedComponentProps>) => {
    interface FromLocalComponentProps { //最终返回的组件的属性对象
        field: string
    }
    interface State { //状态对象
        value: string;
    }
    class FromLocalComponent extends Component<FromLocalComponentProps, State> {
        constructor(props: FromLocalComponentProps) {
            super(props);
            this.state = { value: '' };
        }
        componentWillMount() {
            let value: string | null = localStorage.getItem(this.props.field);
            if (value)
                this.setState({ value });
        }
        render() {
            return <WrappedComponent value={this.state.value} />
        }
    }
    return FromLocalComponent;
}
const UserName = (props: WrappedComponentProps) => (
    <input defaultValue={props.value} />
)
const UserNameFromLocal = fromLocal(UserName);
ReactDOM.render(<UserNameFromLocal field="username" />, document.getElementById('root'));

2.2.2 从ajax中加载

  • 如果我们得到的用户名 zhangsan ,但是要显示中文张三,需要包裹二次
  • 包裹的时候是从内往外一层层包裹
  • 渲染的时候是从外往内渲染
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
const fromLocal = (WrappedComponent, name) => {
    class NewComponent extends Component {
        constructor() {
            super();
            this.state = { id: null };
        }
        componentWillMount() {
            let id = localStorage.getItem(name);
            this.setState({ id });
        }
        render() {
            return <WrappedComponent id={this.state.id} />
        }
    }
    return NewComponent;
}
const fromAjax = (WrappedComponent) => {
    class NewComponent extends Component {
        constructor() {
            super();
            this.state = { value: {} };
        }
        componentDidMount() {
            fetch(`/${this.props.id}.json`).then(response => response.json()).then(value => {
                this.setState({ value });
            });
        }
        render() {
            return <WrappedComponent value={this.state.value} />
        }
    }
    return NewComponent;
}
const UserName = ({ value }) => {
    return <input defaultValue={value.username} />;
}
const UserNameFromAjax = fromAjax(UserName);
const UserNameFromLocal = fromLocal(UserNameFromAjax, 'id');
ReactDOM.render(<UserNameFromLocal />, document.getElementById('root'));

translate.json

{
"zhangsan": "张三"
}

3. render props

  • render prop 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术
  • 具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑
  • render prop 是一个用于告知组件需要渲染什么内容的函数 prop
<DataProvider render={data => (
  <h1>Hello {data.target}</h1>
)}/>

3.1 原生实现

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
interface Props {
}
interface State {
    x: number;
    y: number;
}
class MouseTracker extends React.Component<Props, State> {
    constructor(props: Props) {
        super(props);
        this.state = { x: 0, y: 0 };
    }
    handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
        this.setState({
            x: event.clientX,
            y: event.clientY
        });
    }
    render() {
        return (
            <div onMouseMove={this.handleMouseMove}>
                <h1>移动鼠标!</h1>
                <p>当前的鼠标位置是 ({this.state.x}, {this.state.y})</p>
            </div>
        );
    }
}
ReactDOM.render(<MouseTracker />, document.getElementById('root'));

3.2 children

children是一个渲染的方法

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
interface State {
    x: number;
    y: number;
}
interface Props {
    children: (state: State) => React.ReactNode
}
class MouseTracker extends React.Component<Props, State> {
    constructor(props: Props) {
        super(props);
        this.state = { x: 0, y: 0 };
    }
    handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
        this.setState({
            x: event.clientX,
            y: event.clientY
        });
    }
    render() {
        return (
            <div onMouseMove={this.handleMouseMove}>
                {this.props.children(this.state)}
            </div>
        );
    }
}
ReactDOM.render(<MouseTracker >
    {
        (props: State) => (
            <>
                <h1>移动鼠标!</h1>
                <p>当前的鼠标位置是 ({props.x}, {props.y})</p>
            </>
        )
    }
</MouseTracker >, document.getElementById('root'));

3.3 render属性

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
interface Props {
    render: (state: State) => React.ReactNode
}
interface State {
    x: number;
    y: number;
}
class MouseTracker extends React.Component<Props, State> {
    constructor(props: Props) {
        super(props);
        this.state = { x: 0, y: 0 };
    }
    handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
        this.setState({
            x: event.clientX,
            y: event.clientY
        });
    }
    render() {
        return (
            <div onMouseMove={this.handleMouseMove}>
                {this.props.render(this.state)}
            </div>
        );
    }
}
ReactDOM.render(< MouseTracker render={params => (
    <>
        <h1>移动鼠标!</h1>
        <p>当前的鼠标位置是 ({params.x}, {params.y})</p>
    </>
)} />, document.getElementById('root'));

3.4 HOC

class MouseTracker extends React.Component {
    constructor(props) {
        super(props);
        this.state = { x: 0, y: 0 };
    }
    handleMouseMove = (event) => {
        this.setState({
            x: event.clientX,
            y: event.clientY
        });
    }
    render() {
        return (
            <div onMouseMove={this.handleMouseMove}>
                {this.props.render(this.state)}
            </div>
        );
    }
}
function withMouse(Component) {
    return (
        (props) => <MouseTracker render={mouse => <Component {...props} {...mouse} />} />
    )
}
let App = withMouse(props => (
    <>
        <h1>移动鼠标!</h1>
        <p>当前的鼠标位置是 ({props.x}, {props.y})</p>
    </>
));
ReactDOM.render(<App />, document.getElementById('root'));

结尾

好啦,这期的前端丛林大冒险先到这里啦!这期我们介绍了React的高级用法,这期的知识点比较多而且不容易理解。大家一定要好好啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!

转载自:https://juejin.cn/post/7150680054245097479
评论
请登录