Jetpack Compose对比React Hooks API相似度

 更新时间:2022年08月03日 10:51:06   作者:fundroid  
这篇文章主要为大家介绍了Jetpack Compose对比React Hooks API相似度,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

众所周知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更加简洁:

@Composable
fun 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循环

@Composable
fun 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表达式

@Composable
fun 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函数的形式传入

@Composable
fun Container(children: @Composable () -> Unit) {
  Box {
    children()
  }
}
Container {
  Text("Hello world"!)
}

Context vs Ambient(CompositionLocal)

对于函数组件来说,建议使用props/parameter传递数据,但是允许一些全局数据在组件间共享。React使用Context存放全局数据,Compose使用Ambient(alpha12中已改名CompositionLocal)存放全局数据

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的逻辑:

@Composable
fun 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
}

以上就是Jetpack Compose对比React Hooks API相似度的详细内容,更多关于Jetpack Compose对比React的资料请关注脚本之家其它相关文章!

相关文章

  • Android注册登录实时自动获取短信验证码

    Android注册登录实时自动获取短信验证码

    注册登录或修改密码功能常常需要输入短信验证码,如何自动获取短信验证码,这篇文章就为大家介绍了Androidcv注册登录自动获取短信验证码的实现代码,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • Android通知栏微技巧一些需要注意的小细节

    Android通知栏微技巧一些需要注意的小细节

    这篇文章主要介绍了Android通知栏微技巧,那些你所没关注过的小细节,小编把此文分享到脚本之家平台,需要的朋友可以参考下
    2018-04-04
  • 使用SharedPreferences在Android存储对象详细代码

    使用SharedPreferences在Android存储对象详细代码

    这篇文章主要介绍了使用SharedPreferences在Android存储对象并附上详细代码,下面文章内容较少,大多以代码的形式体现,需要的小伙伴可以参考一下,希望对你有所帮助
    2021-11-11
  • 提示信息控件AlertDialog对话框详解

    提示信息控件AlertDialog对话框详解

    这篇文章主要为大家介绍了提示信息控件AlertDialog对话框的使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • 基于Android如何实现将数据库保存到SD卡

    基于Android如何实现将数据库保存到SD卡

    有时候为了需要,会将数据库保存到外部存储或者SD卡中(对于这种情况可以通过加密数据来避免数据被破解),本文给大家分享Android如何实现将数据库保存到SD卡,对android数据库sd卡相关知识感兴趣的朋友一起学习吧
    2015-12-12
  • Android开发之自定义加载动画详解

    Android开发之自定义加载动画详解

    这篇文章主要介绍了Android开发的自定义加载动画,效果为一个连续的动画,就是这个大圆不停地吞下小圆,文中示例代码讲解详细,感兴趣的可以了解一下
    2022-03-03
  • Android系统中的蓝牙连接程序编写实例教程

    Android系统中的蓝牙连接程序编写实例教程

    这篇文章主要介绍了Android系统中的蓝牙连接程序编写实例教程,包括蓝牙的设备查找及自动配对等各种基础功能的实现,十分给力,需要的朋友可以参考下
    2016-04-04
  • Android 4.4.2 横屏应用隐藏状态栏和底部虚拟键的方法

    Android 4.4.2 横屏应用隐藏状态栏和底部虚拟键的方法

    这篇文章主要介绍了Android 4.4.2 横屏应用隐藏状态栏和底部虚拟键的方法,需要的朋友可以参考下
    2017-01-01
  • Android Studio实现带边框的圆形头像

    Android Studio实现带边框的圆形头像

    这篇文章主要为大家详细介绍了Android Studio实现带边框的圆形头像,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • Android如何获取子View的位置及坐标详解

    Android如何获取子View的位置及坐标详解

    这篇文章主要给大家介绍了关于Android如何获取子View的位置及坐标的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10

最新评论