React Summary (二)

142 阅读3分钟

React中如何逻辑复用?

函数组件

  1. 自定义hooks
  • React中的自定义hooks是功能扩展,允许在函数组件中,复用状态逻辑,并且无需改变组件结构。
  • 本质是js函数,遵循特性规则:必须以use开头;封装业务逻辑,使代码易维护;可使用基础的hook(useState、useRef等);核心要点,把组件的一些逻辑(非状态)提取到可复用的函数中,相同的状态管理+副作用逻辑可在多个组件间共享。
function useForm(initialValues){
    const [values, setValues] = React.useState(initialValues);
    const handleChange = (event) => {
        const {name, value} = event.target;
        setValues({
            ...values,
            [name]: value,
        });
    }
    const handleSubmit = (event) => {
        event.preventDefault();//阻止默认事件
        console.log(values);
    }
    return {
        values,
        handleChange,
        handleSubmit,
    }
}
const LoginForm = () => {
    const {values, handleChange, handleSubmit} = useForm({username:'',possword: ''});
    return (
        <form onSubmit={handleSubmit}>
            <>
                <label>用户名</label>
                <input type='text' name="username" value={values.username} onChange={handleChange} />
            </>
            <>
                <label>密码</label>
                <input type='possword' name="possword" value={values.possword} onChange={handleChange} />
            </>
            <button type='submit'></button>
        </form>
    )
}

高阶组件

  • 高阶组件(HOC,High Order Component)是一个函数式编程函数,参数是一个组件,返回值是一个新组件,新组件具有增强的能力,拥有额外的属性和行为。
  • 允许抽象公共的逻辑,避免代码重复。如 给多个组件添加loading效果。
  • 不直接修改原组件,可保证原始组件的纯净与复用性
const withLoading = (WrappedComponent)=>{
    return ()=>{
        const [isLoading, setIsLoading] = useState(true);
        useEffect(()=>{
            setTimeout(()=>{
                setIsLoading(false);
            }, 2000)
        }, []);
        if(isLoading) {
            return <div>loading.....</div>
        }
        return <WrappedComponent {...props}/>
    }
}
function MyComponent() {
    return <div>内容加载完成</div>
}
const MyComponentWithLoading = withLoading(MyComponent);
function App() {
    return <MyComponentWithLoading />
}

renderProps

  • 使用一个值为函数的props属性,该函数返回要渲染的React元素,通过该方式,可在多个组件中共享业务逻辑。
class MouseTracker extends React.Component{
    state = {x:0, y:0};
    handleMouseMove = (event) =>{
        this.setState({
            x: event.clientX,
            y: event.clientY,
        });
        render(){
            return (
                <div style={{height: '100vh'}} onMouseMove={this.handleMouseMove}>
                    {this.props.render(this.state)}
                </div>
            )
        }
    }
}
const App = () => {
    return (
        <div>
            <h1>请移动你的鼠标</h1>
            <MouseTracker render={({x, y}) => {
                return <div>当前位置:{x},{y}</div>
            }} />
        </div>
    )
}

将上述代码,改写成children实现

{this.props.children(this.state)}

const App = () => {
    return (
        <div>
            <h1>请移动你的鼠标</h1>
            <MouseTracker>
                {
                    ({x, y}) => {
                        return <div>当前位置:{x},{y}</div>
                    }
                }
            </MouseTracker>
        </div>
    )
}

总结:

  • children 与 renderProps原理是一样的,给组件提供一个渲染虚拟DOM的函数用来决定渲染出来的真实DOM。
  • 传递方式、传递位置不一样。props.render,props.children。

类组件

  • 与函数式组件一样,HOC可用于类组件封装并复用逻辑。
  • 高阶组件可组合、级联、串行、链式调用、组合调用,可用多个高阶组件装饰一个组件;慎用高阶组件级联、嵌套(会出现嵌套地狱or回调地狱)。
  • 高阶组件缺点:
  1. 不能透传静态属性。
  2. 不能透传ref属性,因为不会像其他属性一样自动透传给被包裹的属性组件(解决:可使用React.forwardRef进行转发,允许将ref自动转发到另一个组件)。
//提升静态属性
import hoistNonReactStatics from 'hoist-non-react-statics';
function withAuth(WrappedComponent){
    class withAuthComponent extends React.Component{
        isLogin(){
            return !!localStorage.getItem('isLogin');
        }
        render(){
            if(!this.isLogin){
                return <div>请登录后再访问此页面</div>
            }
            return <WrappedComponent />
        }
    }
    //把WrappedComponent类组件上的非React内部静态属性+静态方法传递给withAuthComponent
    return withAuthComponent;
}
class MyComponent extends React.Component{
    static someStaticProperty = 'some value';
    static someStaticMethod = ()=>{
      console.log('someStaticMethod');  
    }
    render(){
        return <div>欢迎来到受保护页面</div>
    }
}
const ProtectedComponent = withAuth(MyComponent);
console.log('ProtectedComponent.someStaticProperty',ProtectedComponent.someStaticProperty);
ProtectedComponent.someStaticMethod();

渲染劫持(高阶组件概念)

  • 在不改变被包裹的组件本身情况下,改变其渲染输出的结果。
  • 条件渲染:根据特定的条件改变渲染的结果or决定是否渲染。
  • 属性操作:可向被包裹的组件传递新的属性or修改旧属性。
  • 结构变更:改变组件的DOM结构与样式。
  • 通过继承实现渲染劫持
//继承实现渲染劫持
class MyComponent extends React.Component{
    render(){
        return (
            <h1 style={this.props.style}>hello,{this.props.name}</h1>
        )
    }
}
function withRenderHijacking(WrappedComponent){
    return class extends WrappedComponent{
        render(){
            if(this.props.loading){
                return <div>loading.....</div>
            }
            //调用旧的render方法返回旧的react元素
            const oldElement = super.render();
            return (
                <div style={{color:'red'}}>
                    <h2>Hijacked</h2>
                    {
                        React.cloneElement(oldElement,{
                            style:{color:'green'}
                        },`hello,hijack`)
                    }
                </div>
            )
        }
    }
}
const HijackedComponent = withRenderHijacking(MyComponent);

类组件继承(不推荐)

设计模式原则:组合优于继承。继承耦合度太高。

Mixin(已废弃)

  • React早期版本中,Mixin用来共享组件逻辑。