类似度99%?Jetpack Compose 与 React Hooks API比照

作者 : 开心源码 本文共4918个字,预计阅读时间需要13分钟 发布时间: 2022-05-14 共233人阅读

众所周知Jetpack Compose设计理念甚至团队成员很多都来自React,在API方面参考了很多React(Hooks) 的设计,通过与React进行比照可以更好地熟习Compose的相关功能。

Compose目前处于alpha版,尽管API还会调整,但是从功能上已经基本对齐了React,不会有大变化,本文基于1.0.0-alpha11

React Component vs Composable

React中Component成为分割UI的基本单元,特别是16.8之后Hooks引入的函数组件,相对于类组件更利于UI与逻辑解耦。函数组件是一个接受Props作为参数并返回JSX node的函数:

function Greeting(props) {  return <span>Hello {props.name}!</span>;}复制代码

Compose同样使用函数作为组件:增加了@Composable注解的函数。而且借助Kotlin的DSL实现公告式语法,而无需额外引入JSX等其余标记语言,相对于React更加简洁:

@Composablefun Greeting(name: String) {  Text(text = "Hello $name!")}复制代码

JSX vs DSL

DSL相对于JSX更加简洁,可以直接使用原生语法表示各种逻辑。

loop

例如在JSX中实现一个循环逻辑,需要两种语言混编

function NumberList(props) {  return (    <ul>      {props.numbers.map((number) => (        <ListItem value={number} />      ))}    </ul>  );}复制代码

DSL中的循环就是普通的for循环

@Composablefun NumberList(numbers: List<Int>) {  Column {    for (number in numbers) {      ListItem(value = number)    }  }}复制代码

If statement

JSX 使用三元运算符表示条件

function Greeting(props) {  return (    <span>      {props.name != null        ? `Hello ${props.name}!`        : 'Goodbye.'}    </span>  );}复制代码

DSL直接使用IF表达式

@Composablefun Greeting(name: String?) {  Text(text = if (name != null) {    "Hello $name!"  } else {    "Goodbye."  })}复制代码

key component

React和Compose都可以通过key来标记列表中的特定组件,缩小重绘范围。

JSX使用key属性

<ul>  {todos.map((todo) => (    <li key={todo.id}>{todo.text}</li>  ))}</ul>复制代码

DSL使用key组件来标识Component

Column {  for (todo in todos) {    key(todo.id) { Text(todo.text) }  }}复制代码

Children Prop vs Children Composable

前面提到,React与Compose都使用函数组件创立UI,区别在于一个使用DSL,另一个依靠JSX。

React中,子组件通过props的children字段传入

function Container(props) {  return <div>{props.children}</div>;}<Container>  <span>Hello world!</span></Container>;复制代码

Compose中,子组件以@Composable函数的形式传入

@Composablefun Container(children: @Composable () -> Unit) {  Box {    children()  }}Container {  Text("Hello world"!)}复制代码

Context vs Ambient

对于函数组件来说,建议使用props/parameter传递数据,但是允许少量全局数据在组件间共享。React使用Context存放全局数据,Compose使用Ambient存放全局数据

createContext : ambientOf

React使用createContext创立Context:

const MyContext = React.createContext(defaultValue);复制代码

Compose使用ambientOf创立Ambient:

val myValue = ambientOf<MyAmbient>()复制代码

Provider : Provider

React和Compose中都使用Provider注入全局数据,供子组件访问

<MyContext.Provider value={myValue}>  <SomeChild /></MyContext.Provider>复制代码
Providers(MyAmbient provides myValue) {  SomeChild()}复制代码

useContext : Ambient.current

React中子组件使用useContext hook访问Context

const myValue = useContext(MyContext);复制代码

Compose中子组件通过单例对象访问Ambient

val myValue = MyAmbient.current复制代码

useState vs State

无论React还是Compose,状态管理都是至关重要的。

React使用useState hook创立State

const [count, setCount] = useState(0);<button onClick={() => setCount(count + 1)}>  You clicked {count} times</button>复制代码

Compose使用mutableStateOf创立一个state,还可以通过by代理商的方式获取

val count = remember { mutableStateOf(0) }Button(onClick = { count.value++ }) {  Text("You clicked ${count.value} times")}复制代码

还可以通过解构分别获取get/set

val (count, setCount) = remember { mutableStateOf(0) }Button(onClick = { setCount(count + 1) }) {  Text("You clicked ${count} times")}复制代码

或者者通过by代理商

var count : Int  by remember { mutableStateOf(false) }Button(onClick = { count++ }) {  Text("You clicked ${count} times")}复制代码

Compose创立state时往往会remeber{ } 避免重绘时的反复创立state,相当于useMemo

useMemo vs remember

React使用useMemo hook用来保存那些不能随重绘反复计算的值,只有参数变化时才会重新计算。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);复制代码

Compose中同样功能使用remember实现,同样通过参数作为重新计算的判断条件

val memoizedValue = remember(a, b) { computeExpensiveValue(a, b) }复制代码

useEffect vs SideEffect

函数组件满足纯函数的要求:无反作用、无状态、即便屡次运行也不会产生影响。但是总有少量逻辑不能以纯函数执行,例如 生命周期回调、日志、订阅、计时等,只能在特定时机执行,不能像一个纯函数那样可以执行屡次而不产生反作用。

React中,useEffect 提供一个hook点,会在每次render时执行。注意 这不同于直接写在外面,当diff没有变化时不需要重新render,就不需要执行useEffect了

useEffect(() => {  sideEffectRunEveryRender();});复制代码

Compose中使用SideEffect解决反作用(早期版本是onCommit{ }

SideEffect {  sideEffectRunEveryComposition()}复制代码

useEffect(callback, deps) :DisposableEffect

跟useMemo一样可以接受参数,每次render时,只有当参数变化时才执行:

useEffect(() => {  sideEffect();}, [dep1, dep2]);复制代码

只在第一次render时执行的逻辑(相当于onMount),可以使用如下形式解决:

useEffect(() => {  sideEffectOnMount();}, []);复制代码

Compose中使用DisposableEffect:

DisposableEffect(   key1 = "",   ...) {  onDispos{}}复制代码

Clean-up function : onDispose

useEffect通过返回一个function进行后解决

useEffect(() => {  const subscription = source.subscribe();  return () => {    subscription.unsubscribe();  };});复制代码

DisposableEffect通过一个DisposableEffectDisposable进行后解决:

DisposableEffect() {  val dispose = source.subscribe()  onDispose { //返回DisposableEffectDisposable     dispose.dispose()  }}复制代码

Hook vs Effect

React允许自己设置Hooks封装可复用逻辑。Hooks可以调用useState、useEffect等其余hooks放方法,在特定的生命周期完成逻辑。自己设置Hooks都使用useXXX的形式来命名

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;}复制代码

Compose没有命名上的要求,任何一个@Composable函数就可被用来实现一段可复用的解决Effect的逻辑:

@Composablefun friendStatus(friendID: String): State<Boolean?> {  val isOnline = remember { mutableStateOf<Boolean?>(null) }  DisposableEffect {    val handleStatusChange = { status: FriendStatus ->      isOnline.value = status.isOnline    }    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange)    onDispose {        ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange)    }  }  return isOnline}
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 类似度99%?Jetpack Compose 与 React Hooks API比照

发表回复