原文地址:tigeroakes.com/posts/react…
原文作者:tigeroakes.com/
发布时间:2020年7月20日
我在一个个人项目上试用了Jetpack Compose,很喜欢这个API。Compose是一个相当大的API转变,我发现我的React知识比我的Android知识更有帮助。也许这就是React Native开发者来取代原生Android开发者的原因。
两个库中的许多概念和函数工作原理相同,但名称不同。下面是我看到的术语以及解释的汇编。Jetpack Compose还在变化,这个列表是基于0.1.0-dev14版本的。
子元素Prop > 子元素Composable
React和Compose都将要显示在另一个UI组件中的元素称为子元素。
React在一个名为children的特殊道具下,通过值来传递子元素。
function Container(props) {
return <div>{props.children}</div>;
}
<Container>
<span>Hello world!</span>
</Container>
Jetpack Compose 传递可合成函数,因为函数本身不返回任何东西。
@Composable
fun Container(children: @Composable () -> Unit) {
Box {
children()
}
}
Container {
Text("Hello world"!)
}
语境 > 环境
虽然大多数数据都是通过组件树作为道具/参数传递的,但有时这种模式可能会很麻烦。React包含Context API来共享这些数据。Compose使用Ambient来完成同样的事情。
createContext > ambientOf
一个React Context是用React.createContext创建的,而Jetpack Compose ambient是用ambientOf创建的。
Provider > Provider
在React和Compose中都可以使用 "Provider "来控制值。
<MyContext.Provider value={myValue}>
<SomeChild />
</MyContext.Provider>
Providers(MyAmbient provides myValue) {
SomeChild()
}
useContext > Ambient.current
访问React上下文值是使用useContext钩子完成的。
const myValue = useContext(MyContext);
访问一个Ambient的值是通过使用.current getter来完成的。
val myValue = MyAmbient.current
useEffect > onCommit
突变、订阅、定时器、日志和其他副作用都不允许放在UI组件的主体内。它们必须放在一个回调函数里面,React和Jetpack Compose都会在正确的点上调用。
React有一个useEffect钩子,可以在每次渲染时运行副作用。
useEffect(() => {
sideEffectRunEveryRender();
});
Compose有onCommit效果,可以在每次构图时运行侧效果。
onCommit {
sideEffectRunEveryComposition()
}
清理函数 > onDispose
副作用经常会产生资源,一旦UI组件未被使用,就需要清理。React允许useEffect函数返回第二个函数,即清理函数。
useEffect(() => {
const subscription = source.subscribe();
return () => {
subscription.unsubscribe();
};
});
Jetpack Compose在onCommit中公开了一个onDispose函数,它在效果离开合成时运行。
onCommit {
val subscription = source.subscribe()
onDispose {
subscription.unsubscribe()
}
}
useEffect(callback, deps) > onCommit(input, callback)
useEffect和onCommit都默认在每次渲染/合成后运行。然而,副作用也可以只在其依赖关系发生变化时运行。
React允许向useEffect传递第二个参数,并附上依赖关系列表:
useEffect(() => {
sideEffect();
}, [dep1, dep2]);
Jetpack Compose允许在回调前将输入作为参数传递:
onCommit(input1, input2) {
sideEffect()
}
useEffect(callback, []) > onActive(callback)
副作用也可以只在UI组件首次显示时运行。
当一个空的依赖列表被传递给useEffect时,副作用只运行一次。
useEffect(() => {
sideEffectOnMount();
}, []);
Jetpack Compose有一个单独的onActive效果。
onActive {
sideEffectOnActive()
}
钩子 > 效果
React让你可以构建自己的钩子,将组件逻辑提取到可重用的函数中。它们可以使用其他钩子,如useState和useEffect来封装与组件生命周期相关的逻辑。
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
在Jetpack Compose中,@Composable函数被用作相当于钩子的功能(同时也是相当于组件的功能)。这些可组合函数,有时也被称为 "效果 "函数,通常以小写字母而不是大写字母开头。
@Composable
fun friendStatus(friendID: String): State<Boolean?> {
val isOnline = state<Boolean?> { null }
onCommit {
val handleStatusChange = { status: FriendStatus ->
isOnline.value = status.isOnline
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange)
onDispose {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange)
}
}
return isOnline
}
关键字Prop > 关键字Composable
关键字是用来帮助React和Jetpack Compose识别哪些项目发生了变化、被添加或被删除。它们必须在你在UI组件中显示的项目列表中是唯一的。
React有一个名为key的特殊字符串道具。
<ul>
{todos.map((todo)=>)
<li key={todo.id}>{todo.text}</li>。
)}
</ul>
Jetpack Compose有一个特殊的实用工具,叫做key,可以接受任何输入。
Column {
for (todo in todos) {
key(todo.id) { Text(todo.text) }
}
}
在Compose中可以将多个输入传入key,而React只需要一个字符串(虽然可以将多个字符串连在一起)。
.map > For Loop
由于React通过值来传递元素,所以通常使用array.map()来创建对应于数组的元素。map回调中返回的元素可以嵌入到JSX中。
function NumberList(props) {
return (
<ul>
{props.numbers.map((number) =>
<ListItem value={number} />
)}
</ul>
);
}
在Jetpack Compose中的可组合UI函数会发出其他UI可组合函数,并且不返回任何东西。因此,可以使用一个简单的for循环来代替.map()。
@Composable
fun NumberList(numbers: List<Int>) {
Column {
for (number in numbers) {
ListItem(value = number)
}
}
}
事实上,可以使用任何迭代方法,如.forEach()。
useMemo > remember
React允许只有在组件内部的某些依赖关系发生变化时,才会通过useMemo钩子重新计算值。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Jetpack Compose有一个类似的函数,名为remember,它只在输入改变时重新计算一个值。
val memoizedValue = remember(a, b) { computeExpensiveValue(a, b) }
React组件 > Composable
在React中,组件用于将UI分割成独立的、可重用的部分。它们可以采用函数的形式,接受一个props参数并返回一个React节点。
function Greeting(props) {
return <span>Hello {props.name}!</span>;
}
在Jetpack Compose中,Composable函数是用于将用户界面分割成独立的、可重复使用的构件。它们是带有 @Composable 注释的函数,可以接受任何数量的参数。
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
这两个库也将这些概念称为UI组件。然而,Jetpack Compose也使用可合成函数来实现其他功能,请看hook > effect和key的例子。
渲染 > 合成
一旦UI组件内部的数据发生了变化,库必须调整在屏幕上呈现的内容。React将其称为渲染,而Jetpack Compose将其称为合成。
调和器 > 合成器
在内部,React需要弄清楚当一个组件被渲染时有什么变化。这个差异的算法叫做 "Reconciler"。React Fiber指的是新的Fiber Reconciler的发布,它取代了旧的算法。
Jetpack Compose的diffing是用Composer完成的。它决定了每次composable完成组成时节点的变化。
状态 > 状态
React和Compose都将你要突变的本地变量称为 "state"。
useState > state
在React中创建一个新的状态变量是通过useState钩子完成的。它返回一个包含值和setter的元组。
const [count, setCount] = useState(0);
<button onClick={() => setCount(count + 1)}>
You clicked {count} times
</button>
Compose使用state函数返回一个MutableState对象,其中包含一个带有getter和setter的变量。
val count = state { 0 }
Button(onClick = { count.value++ }) {
Text("You clicked ${count.value} times")
}
Storybook > 预览
Storybook工具可以帮助你通过创建 "故事 "在网络上独立预览React组件。Jetpack Compose中的@Preview注解可以让你构建可以在Android Studio中预览的可组合示例。
三元操作符 > If语句
React组件经常使用三元运算符(cond ? a : b)来有条件地渲染组件,因为成功的分支会被返回(与JavaScript中的if语句不同)。
function Greeting(props) {
return (
<span>
{props.name != null
? `Hello ${props.name}!`
: 'Goodbye.'}
</span>
);
}
Kotlin没有三元运算符,因为if语句确实会返回成功分支的结果。由于Kotlin中的if语句就像JavaScript中的三元操作符一样,所以不需要第二个变体。
@Composable
fun Greeting(name: String?) {
Text(text = if (name != null) {
"Hello $name!"
} else {
"Goodbye."
})
}
通过( www.DeepL.com/Translator )(免费版)翻译