今天看到朋友圈里说今天是回文数天,20211202
,适合发朋友圈。回文数是经典的编程题目,既然说适合发文章,那我今天也要发一篇。
React继续在看,一个官方不推荐但很多时候又比较有用的特性就是Context上下文,熟悉这个词的朋友都知道,这个就相当于环境的意思。React层层单向数据流动固然好,但如果要接收数据的组件很深,或者有一批组件都需要接受数据,层层传递显得太繁琐,这个时候就用到上下文了。
创建Context
对象
创建的方法很简单,使用React.createContext()
方法,要传入一个参数作为默认值。这里可以写一个单独的UserContext.js
文件:
import React from "react";
const UserContext = React.createContext({user:"minko"});
export default UserContext;
这里传入了一个对象当做初始值,也可以是其他类型的数据。
标明使用Context
对象的范围
所谓范围,其实也就是那些组件需要接收这个上下文信息,这就需要用一个特殊的组件,包在这些组件的外层,写法如下:
import React from "react";
import UserContext from "./UserContext";
import Text from "./Text";
import Title from "./Title";
function App() {
return (
<UserContext.Provider value={{user:'saner'}}>
<Text/>
<Title/>
</UserContext.Provider>
);
}
export default App;
这个特殊组件就是上下文对象.Provider
,被这个特殊组件包裹的Text
和Title
组件就是可以接收到上下文中的信息的组件。这个组件特别之处在于那个value
属性。
特殊组件的value
属性
虽然前边我们传入了默认值,但实际上传递给Text
和Title
组件上下文中的值,是特殊组件的value
属性的值,也就是{user:'saner'}
。如果不给value
传值,则Text
和Title
实际拿到的是undefined
,这很不好,所以你会看到WebStorm IDE给出的提示是value
必须传值。
那么你可能会问,前边为什么要提供默认值,是多此一举吗?答案是,如果没有对应的Provider
组件,那么在这个应用里所有使用到了UserContext
上下文对象的组件,接收到的就是默认值,这个适合把一个静态的东西在整个应用内广播的场景;一些不会变化的基础设置,就可以通过这样的方法来让所有组件使用。
获取上下文对象中的值
有两种方法来获取上下文对象中的值,分别是使用特殊的Consumer
组件和使用this.context
。
使用Consumer
组件
在上边提到的Text
组件中,就使用了Consumer
组件,写法如下:
import {Component} from 'react';
import UserContext from "./UserContext";
class Text extends Component {
render() {
return (
<UserContext.Consumer>
{(value)=>(<div>{value.user}</div>)}
</UserContext.Consumer>
);
}
}
export default Text;
这里的核心就是UserContext.Consumer
组件,在其内部,提供一个函数并渲染结果,这个函数的参数value
(可以叫任意名称)就是上下文中的那个数据,在这个例子中,value
就是UserContext.Provider
组件中的value属性传入的{user:'saner'}
这个对象,所以渲染其user
属性,得到saner
。
这里其实可以把(value)=>(<div>{value.user}</div>)
看成是一个组件的代码,外边加上{}
实际就是渲染组件,本质上和一个React
组件没有任何区别,因为React
组件就是函数,渲染就是函数的运行求值结果。
使用this.context
Title
组件中使用了this.context
,这需要特殊的写法,代码如下:
import {Component} from 'react';
import UserContext from "./UserContext";
class Title extends Component {
static contextType = UserContext;
render() {
return (
<div>
{this.context.user}
</div>
);
}
}
export default Title;
其中这一句static contextType = UserContext;
是特殊写法,前边的变量名称必须是contextType
,采取静态变量的方式,将上下文对象赋给这个变量。
类里边写了这句话之后,在类中,this.context
就指向UserContext
中的值,也就是UserContext.Provider
组件中的value属性传入的{user:'saner'}
这个对象,所以渲染之后可以得到saner
。
两个组件都可以正常获取值,最终渲染的结果是两行saner
。
不存在Provider
的情况
上边的例子看完之后,来看看之前说的,如果找不到对应的Provider
会出现什么情况呢,在App
中去掉Provider
,改成使用Fragment
:
import React from "react";
import Text from "./Text";
import Title from "./Title";
function App() {
return (
<>
<Text/>
<Title/>
</>
);
}
export default App;
这个时候,就会使用创建UserContext
时候的默认值,也就是minko
了,最终结果是渲染两行minko
。
这里的<></>
是React
的Fragment
用法,很多时候不想只返回一个元素,而是想返回多个元素,就用<></>
进行包裹即可,React
在挂载元素内部会把<></>
包裹的元素都渲染上。
一个一个知识点攻破吧,虽然React
的原理好懂,但是JS
本身就很灵活,很多时候写起来不知道代码该如何组织,一方面是因为经验少,另一方面就是React
实际上是函数式编程,逻辑抽象的层次比较高。最近又新买了一本不错的书:深入理解React Router:从原理到实践,看到Hook
那部分就有点懵了 ,因为作者的抽象层次是比较高的,真的是漫漫长路啊。
女儿寒假的编程班也报好了,到了三年级就准备用Python
了,Python
作为入门语言还是不错的,写法很像C,可以写的很具体,也可以立刻提高抽象程度,早期就可以来教函数即对象这种思想,柯里化之类也比较好用Python
代码来展示,看看著名的CS61A就知道了。如果用Java
上来就教人函数式编程,看到诸如
Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;
估计直接就把新人吓跑了。。。。。。