React Native 知识点分享

527 阅读5分钟

1. 函数式组件的 useState

如果要对变量进行修改,并且修改后马上要获取该值,一般会这样使用:

const [count, setCount] = useState(0);
setCount(count + 1);
console.log(count);

但是这样是拿不到立即更新后的值的,正确的方式应该是使用回调函数:

const [count, setCount] = useState(0);
setCount((value) => {
    value++;
    console.log(value);
    return value;
});

2. 类组件的 setState

在类组件中的 setState 方法只会对涉及到的值进行修改,未涉及到的值是不会改变的。

class Demo extends Component<Props> {
    constructor(props) {
        super(props);
        this.state = {
            count: 0,
            scale: 1,
        }
    }
    
    change = () => {
        this.setState({
            count: 1
        });
    }
}

执行 change 方法后,scale 的值不变,而 count 的值变为 1。

且如果想要在改变后立马获取最新的值,直接在 this.setState 后获取是得不到的,因为该方法是异步的,一般通过 shouldComponentUpdate 或者 componentWillUpdate 获取。但是推荐使用回调函数:

change = () => {
    this.setState({
        count = 1
    }, () => {
        console.log(this.state.count); // 1
    });
}

3. useEffct hooks

在类组件中有生命周期这个概念,且有许多生命周期的钩子函数,但在函数式组件中,是没有这个概念的,但是相应的,存在 useEffect 这个 hooks 可以实现生命周期的效果。

useEffect(() => {})

componentWillUpdate这个钩子函数在组件接收到新的 props 或者 state 但还没有 render 时被调用,且在初始化时不会被调用。useEffect(() => {})会在任意属性变动时调用,当 useState 中或者传入的 props 中属性有改变的,则会调用 useEffect 中的回调函数,也就和componentWillUpdate的执行时机类似,但是不同的地方是,useEffect(() => {})会在初始化时被调用。

useEffect(() => {}, [])

componentDidMount在第一次渲染后调用。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。一般在这个方法中调用setTimeout, setInterval或者发送 AJAX 请求等操作(防止异步操作阻塞 UI)。useEffect(() => {}, [])只会在初始化时,也即是第一次渲染后调用。

useEffect(() => {}, [a, b])

该函数会在 a 和 b 变化时执行回调函数,也就是监听 a 和 b。

useEffect(() => { return () => {} }, [])

componentWillUnmount在组件从 DOM 中移除之前立刻被调用。而useEffect(() => { return () => {} }, [])会在组件被卸载时执行返回的函数,两者基本一致。

4. img 标签复用问题

通过 HTML 中img标签加载的图片,浏览器默认情况下会将其缓存起来,当我们从 JS 的代码中创建的imgDOM 对象通过设置src再去访问同一个图片时,浏览器就不会再发起新的请求,而是直接访问缓存的图片。但是由于 JS 中的imgDOM 对象设置了crossorigin,也就意味着它将要以CORS的方式请求,但缓存中的图片显然不是的,所以浏览器会直接拒绝,甚至连网络请求都没有发起,也就导致在 JS 中创建的imgDOM 对象无法显示。

解决的办法是让 HTML 中的img标签和 JS 中imgDOM 对象的访问都走跨域访问的方式:crossOrigin = "anonymous",这样既可以解决跨域访问的问题,也可以解决跨域图片在canvas中的复用问题。

5. 改变 icon 图片的颜色

我们在写页面时,常常会用到会多图标 icon,通常也会遇到主题色配置的问题,需要所有图标都根据主题色来显示不同的颜色。对此,Web 端和 React Native 端对此有着不同的解决方法。

Web 端可以使用 CSS 属性中的filter: drop-shadow(x, y, color)属性,其中xy代表偏移量,color代表改变后的颜色,该属性可以实现将 icon 图片的颜色改为指定 color 的颜色。

React Native端可以使用 CSS 中的tinyColor: color属性,该属性可以实现将 icon 图片的颜色改为指定 color 的颜色。

需要注意的是,两种方式指定的颜色都会替换掉图片中的所有非透明区域,而基本上 icon 的图片都是由专门的 UI/UED 切出来的,所以一般非内容区域都是透明的。

6. useRef hooks

useRef 的官方定义如下:

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

一般情况下它可以用来获取组件实例对象或者是 DOM 对象。但除了传统的用法之外,它还可以“跨渲染周期”保存数据。换句话说,useRef在 hook 中的作用,正如官网说的,它像一个变量,类似于 this,你可以存放任何东西,而useRef每次都会返回相同的引用。

在一个组件中有什么东西可以跨渲染周期,也就是在组件被多次渲染之后依旧不变的属性?第一个想到的应该是state。没错,一个组件的state可以在多次渲染之后依旧不变。但是,state的问题在于一旦修改了它就会造成组件的重新渲染。但是useRef就可以跨越渲染周期存储数据,而且对它修改也不会引起组件渲染。

export default function MyApp(props) {
  const [count, setCount] = useState(0);

  const timer = useRef();
  
  const addCount = () => {
      setCount(count => count + 1);
  }
  
  useEffect(() => {
    timer.current = setInterval(addCount, 1000); 
  }, []);
  
  useEffect(()=>{
      if(count > 10){
          clearInterval(timer.current);
      }
  }, [count]);
  
  return (
    <>
      <button onClick={addCount}>Count: {count}</button>
    </>
  );
}

在上面的例子中,使用ref对象的current属性来存储定时器的 ID,这样便可以在多次渲染之后依旧保存的是定时器 ID,而不会因为重新渲染导致定时器 ID 被改变,从而能正常清除定时器。

7. useMemo hooks

useMemouseEffect类似,依赖项变化时会执行其中的回调函数。

传入 useMemo 的函数会在依赖项变化时的渲染期间执行,一般不在这个函数内部执行与渲染无关的操作,这类的操作属于 useEffect 的适用范畴,而非 useMemo

如果没有提供依赖项数组,useEffect在每次有值改变时都会计算新的值。而当useMemo没有提供依赖项时,其会在每次渲染时都会计算新的值。