四、React组件插槽用法
React中的插槽(slot)
- 在开发中 我们抽取了一个组件 但是为了让这个组件具备更强的通用性 我们不能将组件中的内容限制为固定的div span等等这些元素
- 我们应该让使用者可以决定某一块区域到底存放什么内容
- 这种需求在Vue当中有一个固定的做法是通过slot来完成的 那么在React中呢?
- React对于这种需要插槽的情况非常灵活 有两种方案可以实现
- 组件的children子元素
- props属性传递React元素
children实现插槽
- 每个组件都可以获取到props.children: 它包含组件的开始标签和结束标签之间的内容.
props实现插槽
- 通过children实现的方案虽然可行 但是有一个弊端: 通过索引值获取传入的元素很容易出错 不能精准的获取传入的原生
- 另外一个种方案就是使用 props 实现
- 通过具体的属性名 可以让我们在传入和获取时更加的精准
五、React非父子的通信
Context应用场景
- 非父子组件数据的共享:
- 在开发中 比较常见的数据传递方式是通过props属性自上而下(由父到子)进行传递
- 但是对于有一些场景: 如一些数据需要在多个组件中进行共享(地区偏好、UI主题、用户登录状态、用户信息等)
- 如果我们在顶层的App中定义这些信息 之后一层层传递下去 那么对于一些中间层不需要数据的组件来说 是一种冗余的操作
- 我们实现一个一层层传递的案例:
- 我这边顺便补充一个小的知识点:Spread Attributes
但是 如果层级更多的话 一层层传递是非常麻烦 并且代码是非常冗余的
- React提供了一个API:Context
- Context 提供了一种在组件之间共享此类值的方式 而不必显式地通过组件树的逐层传递 props
- Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据 如 当前认证的用户 主题或首选语言
Context相关API
React.createContext
- 创建一个需要共享的Context对象
- 如果一个组件订阅了Context 那么这个组件会从离自身最近的那个匹配的 Provider 中读取到当前的context值
- defaultValue是组件在顶层查找过程中没有找到对应的Provider 那么就使用默认值
Context.Provider
- 每个 Context 对象都会返回一个 Provider React 组件 它允许消费组件订阅 context 的变化
- Provider 接收一个 value 属性 传递给消费组件
- 一个 Provider 可以和多个消费组件有对应关系
- 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据
- 当 Provider 的 value 值发生变化时 它内部的所有消费组件都会重新渲染
Class.contextType
- 挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象
- 这能让你使用 this.context 来消费最近 Context 上的那个值
- 你可以在任何生命周期中访问到它,包括 render 函数中
Context.Consumer
- React 组件也可以订阅到 context 变更.这能让你在 函数式组件 中完成订阅 context
- 这里需要 函数作为子元素(function as child)这种做法
- 这个函数接收当前的 context 值,返回一个 React 节点
- 什么时候使用Context.Consumer?
- 当使用value的组件是一个函数式组件时
- 当组件中需要使用多个Context时
六、setState的使用详解
为什么使用setState
开发中我们并不能直接通过修改state的值来让界面发生更新
- 因为我们修改了state之后 希望React根据最新的State来重新渲染界面 但是这种方式的修改React并不知道数据发生了变化
- React并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化
- 我们必须通过setState来告知React数据已经发生了变化
在组件中并没有实现setState的方法,为什么可以调用呢?
- 原因很简单,setState方法是从Component中继承过来的
setState异步更新
- setState的更新是异步的?
- 最终打印结果是Hello World
- 可见setState是异步的操作 我们并不能在执行完setState之后立马拿到最新的state的结果
- 为什么setState设计为异步呢?
- setState设计为异步其实之前在GitHub上也有很多的讨论
- React核心成员(Redux的作者)Dan Abramov也有对应的回复 有兴趣的同学可以参考一下
- 总结:setState设计为异步,可以显著的提升性能
- 如果每次调用 setState都进行一次更新 那么意味着render函数会被频繁调用 界面重新渲染 这样效率是很低
- 最好的办法应该是获取到多个更新 之后进行批量更新
- 如果同步更新了state 但是还没有执行render函数 那么state和props不能保持同步
- state和props不能保持一致性 在开发中产生很多的问题
如何获取异步的结果
- 如何可以获取到更新后的值呢?
- setState(partialState, callback)
- setState接受两个参数: 第二个参数是一个回调函数 这个回调函数会在更新后会执行
- setState的回调
- 当然,我们也可以在生命周期函数
setState一定是异步吗?(React18之前)
- 在setTimeout中的更新
原生DOM事件
- 其实分成两种情况:
- 在组件生命周期或React合成事件中,setState是异步
- 在setTimeout或者原生dom事件中,setState是同步
setState默认是异步的(React18之后)
- 在React18之后 默认所有的操作都被放到了批处理中(异步处理)
- 如果希望代码可以同步拿到 则需要执行特殊的flushSync操作