首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在kotlin-react useEffect中订阅StateFlow

如何在kotlin-react useEffect中订阅StateFlow
EN

Stack Overflow用户
提问于 2020-07-02 15:02:45
回答 2查看 1K关注 0票数 3

我正在尝试用kotlin 1.4-M2为kotlin-react和functionalComponent创建一个小的反例。该示例应使用kotlinx.coroutines.flow。我正在努力从reacts useEffect钩子中的存储中收集值。

商店:

代码语言:javascript
复制
object CounterModel { // Modified sample from kotlin StateFlow doc
    private val _counter = MutableStateFlow(0) // private mutable state flow
    val counter: StateFlow<Int> get() = _counter // publicly exposed as read-only state flow

    fun inc() { _counter.value++ }
}

组件:

代码语言:javascript
复制
val counter = functionalComponent<RProps> {
    val (counterState, setCounter) = useState(CounterModel.counter.value)

    useEffect(listOf()) {
        // This does not work
        GlobalScope.launch { CounterModel.counter.collect { setCounter(it) } }
    }
    
    div {
        h1 {
            +"Counter: $counterState"
        }
        button {
            attrs.onClickFunction = { CounterModel.inc() }
        }
    }
}

当我直接调用CounterModel.counter.collect { setCounter(it) }时,它会抱怨Suspend function 'collect' should be called only from a coroutine or another suspend function

你将如何实现这个useEffect钩子?

一旦订阅生效,您将如何取消订阅(使用useEffectWithCleanup而不是useEffect)?

EN

回答 2

Stack Overflow用户

发布于 2020-07-03 15:05:20

终于找到了解决方案。我们可以使用onEach为每个新值执行操作,然后使用launchIn 'subscribe‘。这将返回一个可以取消清理的作业:

代码语言:javascript
复制
object CounterStore {
    private val _counter = MutableStateFlow(0)
    val counter: StateFlow<Int> get() = _counter
    
    fun inc() { _counter.value++ }
}

val welcome = functionalComponent<RProps> {
    val (counter, setCounter) = useState(CounterStore.counter.value)

    useEffectWithCleanup(listOf()) {
        val job = CounterStore.counter.onEach { setCounter(it) }.launchIn(GlobalScope)
        return@useEffectWithCleanup { job.cancel() }
    }

    div {
        +"Counter: $counter"
    }
    button {
        attrs.onClickFunction = { CounterStore.inc() }
        +"Increment"
    }
}

我们可以将这个StateFlow逻辑提取到一个自定义的react钩子中:

代码语言:javascript
复制
fun <T> useStateFlow(flow: StateFlow<T>): T {
    val (state, setState) = useState(flow.value)

    useEffectWithCleanup(listOf()) {
        val job = flow.onEach { setState(it) }.launchIn(GlobalScope)
        return@useEffectWithCleanup { job.cancel() }
    }
    
    return state
}

在我们的组件中像这样使用它:

代码语言:javascript
复制
val counter = useStateFlow(CounterStore.counter)

完整的项目可以在here上找到。Flow-Api是非常实验性的,所以这可能不是最终的解决方案:)

票数 2
EN

Stack Overflow用户

发布于 2020-09-20 22:39:26

如果在调用setState之前检查该值没有更改,这一点非常重要,否则渲染会发生两次

代码语言:javascript
复制
external interface ViewModelProps : RProps {
    var viewModel : MyViewModel
}
val App = functionalComponent<ViewModelProps> { props ->
    val model = props.viewModel
    val (state, setState) = useState(model.stateFlow.value)
    useEffectWithCleanup {
        val job = model.stateFlow.onEach {
            if (it != state) {
                setState(it)
            }
        }.launchIn(GlobalScope)
        return@useEffectWithCleanup { job.cancel() }
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62690722

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档