公告

👇👇👇扫我

图片
Skip to content

react 题目

react 引入css的几种方式

遵循规则 -- 不能污染其他组件 -- 支持动态css -- 支持css所有特性 -- 编写起来方便

  • 1 变量式声明 const div1={width:'300px'} 内联样式
  • 2 组件中 import 引入css文件
  • 3 css module.css 文件

react 中常见的钩子

useState

useMemo 计算属性

useCallback 计算函数

useContext 获取上下文对象,传值

javascript
const Ctx = createContext("admin");

const currCtx = useContext(Ctx);
<Ctx.Provider value={'value'}>
    <Child/>
</Ctx.Provider>

const Child = () => {
    return
    <Fragment>
        <Ctx.Consumer>
            {value => value.toString()}
        </Ctx.Consumer>
    </Fragment>

useRef 通过ref获取实例

javascript
const r = useRef(null);
const render = () => {
    return <div ref={r}>this is ref</div>
}
r.current.innerHTML = 'this is set ref'

useEffect 生命周期钩子结合 初次渲染 更新 销毁

useLayoutEffect 重绘前触发生命周期钩子 阻塞更新,解决界面闪动

useReducer dispatch修改

javascript
const [state, dispatch] = useReducer((state, action) => {
    if (action.type === '1') {
        return {count: state.count + 1}
    }
    return {count: state.count + 2}
}, {count: 1})

forwardRef 函数组件不能ref访问 useImperativeHandle 用来给父组件暴露 当前组件的属性

javascript
const component = React.forwardRef((props, ref) => {
    function fn() {
        console.log("子组件的fn")
    }

    useImperativeHandle(ref, () => ({
        fn
    }), [fn]);

    return <div>component</div>
})

useHistory 路由前进后退

useLocation 路由参数获取

生命周期

  1. 初始化
  2. 更新
  3. 销毁
  • 1、getDefaultProps() 设置默认的props,也可以用dufaultProps设置组件的默认属性.

  • 2、getInitialState() 在使用es6的class语法时是没有这个钩子函数的,可以直接在constructor中定义this.state。此时可以访问this.props

  • 3、componentWillMount() 组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。 在渲染前调用,在客户端也在服务端。

  • 4、 render() react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。

  • 5、componentDidMount() 组件渲染之后调用,只调用一次。

在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。

如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作( 防止异步操作阻塞UI)。

  • 6、componentWillReceiveProps(nextProps)

组件初始化时不调用,组件接受新的props时调用。

使用componentWillReceiveProps的时候,不要去向上分发,调用父组件的相关setState方法,否则会成为死循环

在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

  • 7、shouldComponentUpdate(nextProps, nextState)

react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,

如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,

节省大量性能,尤其是在dom结构复杂的时候

返回一个布尔值。在组件接收到新的props或者state时被调用。

在初始化时或者使用forceUpdate时不被调用。

可以在你确认不需要更新组件时使用。

  • 8、componentWillUpdata(nextProps, nextState)

组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state

  • 9、render()

组件渲染

  • 10、componentDidUpdate()

组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。

卸载

  • 11、componentWillUnmount()

组件将要卸载时调用,一些事件监听和定时器需要在此时清除。

生命周期 调用次数 能否使用 setSate() getDefaultProps 1(全局调用一次) 否 getInitialState 1 否 componentWillMount 1 是 render >=1 否 componentDidMount 1 是 componentWillReceiveProps >=0 是 shouldComponentUpdate >=0 否 componentWillUpdate >=0 否 componentDidUpdate >=0 是 componentWillUnmount 1 否

事件的传播和捕获执行顺序

事件捕获阶段 -> 事件执行阶段 -> 事件冒泡阶段。

  1. 事件捕获阶段的处理程序
  2. 事件的目标元素上定义的处理程序
  3. 事件冒泡阶段的处理程序 通过将 options 参数设置为 true,可以在事件捕获阶段执行事件处理程序。这样做可以在事件到达目标元素之前捕获并处理事件,从而实现更精细的事件控制。 需要注意的是,并非所有的事件都支持事件捕获阶段,比如 focus 和 blur 事件就不支持。此外,事件捕获和事件冒泡的默认顺序是不可逆的,可以通过设置 options 参数为 true 来改变它们的顺序,但是不能完全颠倒它们的顺序。

react 合成事件

document 捕获 > body 捕获 > react 组件根节点 捕获(合成) > root 捕获 > inner 节点 捕获(合成)> inner 节点 冒泡 (原生)> inner 节点 冒泡 (合成) root 冒泡 > body 冒泡 > document 冒泡

setState 同步/异步

这是 React 此次版本中最大的破坏性更新,并且无法向下兼容。

React 中的批处理简单来说就是将多个状态更新合并为一次重新渲染,以获得更好的性能,在 React 18 之前,React 只能在组件的生命周期函数或者合成事件函数中进行批处理。默认情况下,Promise、setTimeout 以及原生事件中是不会对其进行批处理的。如果需要保持批处理,则可以用 unstable_batchedUpdates 来实现,但它不是一个正式的 API。

React 18 之前:

javascript
function handleClick() {
    setCount(1);
    setFlag(true);
    // 批处理:会合并为一次 render
}

async function handleClick() {
    await setCount(2);
    setFlag(false);
    // 同步模式:会执行两次 render
    // 并且在 setCount 后,在 setFlag 之前能通过 Ref 获取到最新的 count 值
}

总结

react18 之前

  1. 异步代码块中使用setState 可以同步获取修改后的值
  2. 非异步代码块中获取setState 获取不到修改后的值
  3. react 18 将所有的setState 都改为批量更新 也就是异步的

setState对象式和函数式

javascript
const [count1, setCount1] = useState(0)
const [count2, setCount2] = useState(0)

function app() {
    for (var i = 0; i < 10; i++) {
        setTimeout(() => {
            setCount1(count1 + 1)
            console.log(count1, 'count1');// 输出十次0
        })
    }
    for (var i = 0; i < 10; i++) {
        setTimeout(() => {
            setCount2((old) => old + 1)
            console.log(count1, 'count2');// 输出十次0
        })
    }

    return {
        render() {
            return <div>
                {count1}
                {/* count1 = 1 */}
                {count2}
                {/* count2 = 10 */}
            </div>
        }
    }
}

结论: 函数式setState使用场景: 依赖于上一次更新值,避免异步更新导致的问题

函数式组件和类组件区别

类组件

javascript
import React, {Component} from 'react'

class App extends Component {
    state = {
        number: 0
    }
    handleClick = () => {
        for (let i = 0; i < 5; i++) {
            setTimeout(() => {
                this.setState({number: this.state.number + 1})
                console.log(this.state.number);// 1 2 3 4 5
            }, 1000)
        }
    }

    render() {
        return (
            <div>
                当前的number:{this.state.number}
                {/* 5 */}
                <button onClick={this.handleClick}>点击我</button>
            </div>
        )
    }
}

export default App

函数组件

javascript
import React, {useState} from 'react';

const App = () => {
    const [num, setNumber] = useState(0)
    const handleClick1 = () => {
        for (let i = 0; i < 5; i++) {
            setTimeout(() => {
                setNumber(num + 1)
                console.log(num) // useState 状态每次更新都重新执行函数 0
            }, 1000)
        }
    }
    return (
        <div>
            当前的num值:{num}
            {/* 1 */}
            <br/>
            <button onClick={handleClick1}>改变num</button>
        </div>
    );
};
export default App;

结论

类class组件中,通过一个实例化的class,去维护组件中的各种状态。点击按钮,循环五次setTimeout,同时由于setState没有在react正常的函数里执行上下文,而是位于异步的setTimeout里面,所以批量更新的条件被破坏,可以同步拿到每次setState之后的值 (关于React批量更新可查看: React学习笔记——this.setState的基础使用和不同传参方法详解) 函数Function组件中,没有一个状态去保存这些信息,它只是一个函数,每一次函数上下文执行,所有变量,常量都重新声明,执行完毕,再被垃圾机制回收。所以如上,无论setTimeout执行多少次,都是在当前函数上下文执行,此时num = 0不会变,之后setNumber执行,函数组件重新执行之后,num才变化。

所以, 对于class组件,我们只需要实例化一次,实例中保存了组件的state等状态。对于每一次更新只需要调用render方法就可以。 但是在function组件中,我们每一次更新都是一次新的函数执行,为了保存一些状态,执行一些副作用钩子,react-hooks应运而生,去帮助记录组件的状态,处理一些额外的副作用。

flushSync 用于退出批量更新改为同步更新

javascript
function fn() {
    flushSync(() => {
        setState(0)
    })
    setState(0)
}
阅读量: 0
评论量: 0