当前位置 : 主页 > 手机开发 > android >

Jetpack Compose对比React Hooks API相似度

来源:互联网 收集:自由互联 发布时间:2023-02-01
目录 React Component vs Composable JSX vs DSL loop If statement key component Children Prop vs Children Composable Context vs Ambient(CompositionLocal) createContext : ambientOf Provider : Provider useContext : Ambient.current useState vs
目录
  • React Component vs Composable
  • JSX vs DSL
    • loop
    • If statement
    • key component
  • Children Prop vs Children Composable
    • Context vs Ambient(CompositionLocal)
      • createContext : ambientOf
      • Provider : Provider
      • useContext : Ambient.current
    • useState vs State
      • useMemo vs remember
        • useEffect vs SideEffect
          • useEffect(callback, deps) :DisposableEffect
          • Clean-up function : onDispose
        • Hook vs Effect

          众所周知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的资料请关注自由互联其它相关文章!

          网友评论