前言
由于React Native 使用和React相同的API结构,所以了解学习React很有必要,本节带你快速入门React
import React from 'react';
import {Text} from 'react-native';
const FirstCompose = () => {
return <Text>Hello</Text>;
};
export default FirstCompose;
这是最简单的一个React组件代码,第一步需要通过import
导入你所需要的组件或库,这里就导入了React和Text,通过一个函数就可以声明一个组件,它的返回值可以被渲染。最后一步将你的组件通过export
导出即可。
导入导出属于JavaScript语法,它有多种写法,详细可以查阅资料。https://zh.javascript.info/import-export
关注函数返回<Text>Hello</Text>
:这是React用来描述UI元素的语法(JSX),属于JavaScript语法的一部分
JSX
需要注意的是,JSX语法是被包含在React库中,所以你需要在文件首部import React from 'react'
导入。
由于JSX是属于JavaScript的一部分,所以你仍然可以使用JavaScript的其他语法,只需要用{}包裹起来即可。
import React from 'react';
import {Text} from 'react-native';
const getFullName = (
firstName: string,
secondName: string,
thirdName: string,
) => {
return firstName + ' ' + secondName + ' ' + thirdName;
};
const Cat = () => {
return <Text>Hello, I am {getFullName('Rum', 'Tum', 'Tugger')}!</Text>;
};
export default Cat;
自定义复杂组件
我们也可以把多个组件组合在一起导出,在Android开发中,我们把View放到LinearLayout
, FrameLayout
, RelativeLayout
等父布局中,并对其进行排列组合,在React中我们只需要使用<View>
就可以把多个组件组合起来。<View></View>
也可以简写成<></>
import React from 'react';
import {Text, View} from 'react-native';
const Cat = () => {
return (
<View>
<Text>I am also a cat!</Text>
</View>
);
};
const Cafe = () => {
return (
<View>
<Text>Welcome!</Text>
<Cat />
<Cat />
<Cat />
</View>
);
};
export default Cafe;
在这个例子中,其他文件只需要使用<Cafe>
就可以一次渲染全部组件。其中一个组件包含了另一个组件称为父组件
Prop
上一节只讲了如何定义一个复杂组件,但是并没有提供用户可选的参数。为自定义的组件添加参数很简单,就是为你声明的函数增加参数即可:
import React from 'react';
import {Text, View} from 'react-native';
type CatProps = {
name: string;
};
const Cat = (props: CatProps) => {
return (
<View>
<Text>Hello, I am {props.name}!</Text>
</View>
);
};
const Cafe = () => {
return (
<View>
<Cat name="Maru" />
<Cat name="Jellylorum" />
<Cat name="Spot" />
</View>
);
};
export default Cafe;
使用TypeScript
语法规定参数类型,参数的使用和正常的类调用一样,主要关注调用函数时的传参<Cat name="Maru" />
。这是一种传参的方式,还有一种情况,大家可能会好奇,例如:<Text>Hello</Text>
这种情况下这里的Hello传递以后是属于那个参数呢?
答案是:Hello 是传递给 children
属性的值。children
属性是一个特殊的属性,它允许您在组件中嵌套其他组件或文本。 我们顺便查看一下源码是不是这样。
export interface TextProps extends TextPropsIOS, TextPropsAndroid, AccessibilityProps {
/**
* Specifies whether fonts should scale to respect Text Size accessibility settings.
* The default is `true`.
*/
allowFontScaling?: boolean | undefined;
children?: React.ReactNode;
//...
}
确实可以发现children属性,并且他是React.ReactNode
类型。那我们推断一下是不是
<Text><Text>...</Text></Text>
这样也可以正常显示,甚至做更多自定义呢?
State
到目前为止,我们的页面都是静态的。我们可以使用useState
定义一个可修改的变量,当其改变时,将会触发重新渲染
import React, {useState} from 'react';
import {Button, Text, View} from 'react-native';
type CatProps = {
name: string;
};
const Cat = (props: CatProps) => {
const [isHungry, setIsHungry] = useState(true);
return (
<View>
<Text>
I am {props.name}, and I am {isHungry ? 'hungry' : 'full'}!
</Text>
<Button
onPress={() => {
setIsHungry(false);
}}
disabled={!isHungry}
title={isHungry ? 'Pour me some milk, please!' : 'Thank you!'}
/>
</View>
);
};
export default Cat;
在这个例子中,声明了isHungry这个变量,当点击按钮以后使用setIsHungry改变它的值。触发重新渲染,显示新的文本
useState
const [state, setState] = useState(initialState);
useState接受一个初始值,返回对应的变量和修改变量的方法,依次赋值给const [state, setState]
,这个语法叫做:解构赋值。当调用setState
修改值时,会触发组件使用新值重新进行渲染。
initialState
:不仅可以传递固定的值,还可以传递一个函数,这个函数不能有输入,必须有一个输出。如果你传递的仅是函数名, 那么这个函数只会在初始化第一次调用,如果你是传递的函数调用,那么每次渲染都会调用
function createInitialTodos() {
return ...
}
function TodoList() {
//如果这样传参,那么每次渲染都会重新调用createInitialTodos
const [todos, setTodos] = useState(createInitialTodos());
//如果只传参数名,那么仅会调用一次
const [todos, setTodos] = useState(createInitialTodos);
}
setState
:函数原型为:setState(S)
或者setState((prevState: S) => S)
这说明setState的调用也可以传递函数,函数的入参为上一次的值,需要返回更改后的值。
对于对象,通过Object.is进行比较
//1.
setState(false)
//2.
setState((pre) => {
//do something
let ret = !pre
return ret
})
state
:代表当前的值,初始值是根据你传入的initialState
决定
注意
- React会等待所有的State执行完成以后在批量进行渲染,如果你想立马执行渲染可以使用flushSync
- 当你调用set方法以后不会立马修改state的值,而是等待渲染的时候才会更改
const [name, setName] = useState('Taylor');
function handleClick() {
setName('Robin');
console.log(name); // Still "Taylor"!
}
- 如果你对set传递的参数是函数,那么这个函数会被加入队列中,在下一次渲染时依次执行
import { useState } from 'react';
export default function Counter() {
const [age, setAge] = useState(42);
function increment() {
setAge(a => a + 1);
}
return (
<>
<h1>Your age: {age}</h1>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
</>
);
}
//这里按钮触发会执行三次increment,也就是执行三次setAge,因为传递的是函数,并不会立马执行,
//而是丢入队列,等下次渲染时,依次执行,这样才可以达到依次点击,age + 3
//如果你不使用函数的方式递增,那么是会失败的
function increment() {
setAge(a + 1);
setAge(a + 1);
setAge(a + 1);
}
//set函数执行后age的值不会立马改变,所以这里其实是调用了3此setAge(42 + 1)
//按钮点击后,依旧只增加1
- 对于state为对象或者数组的情况,由于state是只读的的属性,你应该替换它,而不是修改内部值
const [from, setFrom] = useState({firstName:"AAA"})
setForm({
...form,
firstName: 'Taylor'
});
//这里也是一个解构赋值语句,实则是新建了一个对像
useEffect
useEffect(setup, dependencies?)
dependencies
:是一个数组,里面包含了useEffect监听的值,当这些值产生变化后,会调用setup
函数- 如果传递的数组内有值:那么则会在首次渲染时和数组内的值变化后,调用setup函数
- 如果传递的数组是空数组:那么仅会在首次渲染时,调用setup函数
- 如果完全没有传递这个参数:那么每次渲染,都会调用setup函数
setup
:是一个函数,函数可以再返回一个"清理函数",首次渲染时,会调用setup函数,之后渲染如果你提供了清理函数,会先使用旧的dependencies
执行清理函数,然后使用新值执行setup函数。当组件被移除时,会最后调用一次清理函数
import { useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
//清理函数
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}
useRef
useRef(initialValue)
返回一个仅包含current
属性的对象,这个属性的初始值为你设置的initialValue
。当下一次渲染时useRef会返回相同的对象,你可以改变current
的值用来记录一些信息。它与useState
的不同之处在于,当你修改current
时不会触发渲染。
ref的另一个用处是可以控制DOM,如下例子
import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
首先使用null声明一个ref,然后将其赋值给DOM的ref属性。在React创建这个DOM并渲染至屏幕上时,React会设置ref的current为这个DOM。然后就可以通过current操作它。当DOM从屏幕消失时,React会将current置为null。
memo
const MemoizedComponent = memo(SomeComponent, arePropsEqual?)
memo可以让组件当属性未改变时跳过渲染
SomeComponent
:一个你需要处理的组件,memo会返回一个新的一模一样的组件arePropsEqual
:一个用户判断属性是否改变的函数,函数原型为:(preProp, newProp)=> bool
如果未指定该参数,那么会使用object.is