美文网首页程序员
相似度99%?Jetpack Compose 与 React H

相似度99%?Jetpack Compose 与 React H

作者: Android阿南 | 来源:发表于2021-02-18 14:54 被阅读0次

    众所周知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

    对于函数组件来说,建议使用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的逻辑:

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

    相关文章

      网友评论

        本文标题:相似度99%?Jetpack Compose 与 React H

        本文链接:https://www.haomeiwen.com/subject/bqtnxltx.html