提问人:Notchdev66 提问时间:11/10/2023 最后编辑:Mandar PanditNotchdev66 更新时间:11/10/2023 访问量:45
如何避免在重构发生时运行if else代码块?
How to avoid the if else code block to run when recomposition happens?
问:
有一个可组合项,用户可以在其中添加新客户或更新现有客户,我有一个可空的状态(如果是新客户,则可空),还有两个状态或,并且我使用 if else 来显示添加和更新客户的成功和失败的 toast 消息, 问题是,当客户更新时,或者即使添加了新客户,重组也会发生两次,如果 else 代码块触发两次,并在成功更新或添加时显示两次 Toast 消息和两次导航返回AddOrUpdateCustomerRoute
existingcustomer
isCustomerUpdated
isCustomerAdded
这里是 Screen 的代码
@Composable
fun AddOrEditCustomerRoute(
modifier: Modifier = Modifier,
customerId: Long? = null,
viewModel: CustomerViewModel = hiltViewModel(),
navigateBack: () -> Unit
) {
val context = LocalContext.current
viewModel.getCustomer(customerId)
val existingCustomer = viewModel.existingCustomer.collectAsStateWithLifecycle()
val isCustomerAdded = viewModel.isCustomerAdded.collectAsStateWithLifecycle()
val isCustomerUpdated = viewModel.isCustomerUpdated.collectAsStateWithLifecycle()
if (isCustomerAdded.value == true) {
Toast.makeText(context, "Customer Added", Toast.LENGTH_SHORT).show()
navigateBack.invoke()
} else if (isCustomerAdded.value == false) {
Toast.makeText(context, "Failed to add customer! Try again", Toast.LENGTH_SHORT).show()
}
if (isCustomerUpdated.value == true) {
Toast.makeText(context, "CustomerUpdated", Toast.LENGTH_SHORT).show()
navigateBack.invoke()
} else if (isCustomerUpdated.value == false) {
Toast.makeText(context, "Failed to update customer, Try again", Toast.LENGTH_SHORT)
.show()
}
AddOrEditCustomerScreens(
modifier = modifier,
customer = existingCustomer.value,
addCustomer = { name, phone, email ->
viewModel.addCustomer(name, phone, email)
},
updateCustomer = {
viewModel.updateCustomer(it)
},
navigateBack = navigateBack
)
}
@Composable
fun AddOrEditCustomerScreens(
modifier: Modifier = Modifier,
customer: Customer? = null,
addCustomer: (String, String, String) -> Unit,
updateCustomer: (customer: Customer) -> Unit,
navigateBack: () -> Unit
) {
val isEditing = customer != null
val title = if (isEditing) stringResource(id = R.string.update_customer) else stringResource(
id = R.string.add_customer
)
val customerName = remember(customer) {
mutableStateOf(customer?.name ?: "")
}
val phoneNumber = remember(customer) {
mutableStateOf(customer?.phone ?: "")
}
val email = remember(customer) {
mutableStateOf(customer?.email ?: "")
}
Scaffold(
modifier = modifier.fillMaxSize(),
topBar = {
DefaultAppBar(title = title, navigateBack = navigateBack, showSearch = false)
}
) { paddingValue ->
Column(
modifier = Modifier
.padding(paddingValue)
.padding(vertical = 12.dp, horizontal = 16.dp)
) {
OutlinedTextField(
value = customerName.value,
onValueChange = { customerName.value = it },
label = {
TextH70(text = stringResource(id = R.string.customer_name))
},
modifier = Modifier
.fillMaxWidth()
)
OutlinedTextField(
value = phoneNumber.value,
onValueChange = {
if (it.length <= MAX_PHONE_LIMIT) {
phoneNumber.value = it
}
},
label = {
TextH70(text = stringResource(id = R.string.mobile_number_optional))
},
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number
),
modifier = Modifier
.fillMaxWidth()
)
OutlinedTextField(
value = email.value,
onValueChange = { email.value = it },
label = {
TextH70(text = stringResource(id = R.string.email_id))
},
modifier = Modifier
.fillMaxWidth()
)
Button(
enabled = (customerName.value.isNotEmpty() && phoneNumber.value.isNotEmpty()),
onClick = {
if (isEditing)
updateCustomer(
customer!!.copy(
name = customerName.value,
phone = phoneNumber.value,
email = email.value
)
)
else
addCustomer(customerName.value, phoneNumber.value, email.value)
},
modifier = Modifier
.padding(top = 24.dp)
.align(Alignment.End),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
),
shape = RoundedCornerShape(8.dp)
) {
Icon(imageVector = Icons.Filled.PersonAdd, null)
Spacer(modifier = Modifier.width(4.dp))
TextH40(
title,
color = Color.White
)
}
}
}
}
here' 是 viewModel 代码
@HiltViewModel
class CustomerViewModel @Inject constructor(
private val getCustomersUseCase: GetCustomersUseCase,
private val searchCustomersUseCase: SearchCustomersUseCase,
private val getCustomerUseCase: GetCustomerUseCase,
private val addCustomerUseCase: AddCustomerUseCase,
private val updateCustomerUseCase: UpdateCustomerUseCase
) : ViewModel() {
private val _existingCustomer = MutableStateFlow<Customer?>(null)
val existingCustomer = _existingCustomer
private val _customers = MutableStateFlow<PagingData<Customer>>(PagingData.empty())
val customers = _customers
private val _isCustomerAdded = MutableStateFlow<Boolean?>(null)
val isCustomerAdded = _isCustomerAdded
private val _isCustomerUpdated = MutableStateFlow<Boolean?>(null)
val isCustomerUpdated = _isCustomerUpdated
init {
fetchInitCustomers()
}
private fun fetchInitCustomers() {
viewModelScope.launch {
getCustomersUseCase.perform()
.map { pagingData ->
pagingData.map {
it.toCustomer()
}
}.cachedIn(viewModelScope)
.collect {
_customers.value = it
}
}
}
fun searchCustomers(query: String) {
viewModelScope.launch {
searchCustomersUseCase.perform(query)
.map { pagingData -> pagingData.map { it.toCustomer() } }.cachedIn(viewModelScope)
.collect {
customers.value = it
}
}
}
fun getCustomer(id: Long?) {
viewModelScope.launch {
if (id != null) {
_existingCustomer.value = getCustomerUseCase.perform(id)
} else {
_existingCustomer.value = null
}
}
}
fun addCustomer(name: String, phone: String, email: String) {
viewModelScope.launch {
val request = AddCustomerRequest(
name = name,
phone = phone,
email = email,
amountDue = null
)
_isCustomerAdded.value = addCustomerUseCase.perform(request)
}
}
fun updateCustomer(customer: Customer) {
viewModelScope.launch {
val request = UpdateCustomerRequest(
id = customer.id,
name = customer.name,
phone = customer.phone,
email = customer.email,
amountDue = customer.amountDue
)
_isCustomerUpdated.value = updateCustomerUseCase.perform(customer.id, request)
}
}
}
我认为状态的提取可以在这里做得更好,但我就是无法理解如何做到这一点,这是 Compose 的新手,这在 XML 中是小菜一碟,但在 Compose 中做事对我来说是新的,因为我正在努力学习它
我尝试使用像 LauncedEffect 这样的副作用,但它根本没有帮助,代码甚至没有运行,所以找不到太多关于我在这里能做什么的信息
LaunchedEffect(isCustomerAdded) {
if (isCustomerAdded.value == true) {
Toast.makeText(context, "Customer Added", Toast.LENGTH_SHORT).show()
navigateBack.invoke()
} else if (isCustomerAdded.value == false) {
Toast.makeText(context, "Failed to add customer! Try again", Toast.LENGTH_SHORT).show()
}
}
LaunchedEffect(isCustomerUpdated) {
if (isCustomerUpdated.value == true) {
Toast.makeText(context, "CustomerUpdated", Toast.LENGTH_SHORT).show()
navigateBack.invoke()
} else if (isCustomerUpdated.value == false) {
Toast.makeText(context, "Failed to update customer, Try again", Toast.LENGTH_SHORT)
.show()
}
}
答:
尝试添加到密钥。.value
LaunchedEffect
LaunchedEffect(isCustomerAdded.value)
或者保留,但使用 切换到属性委派。LaunchedEffect
by
val isCustomerAdded by viewModel.isCustomerAdded.collectAsStateWithLifecycle()
评论
这将调用每次重构发生,
若要确保代码调用仅执行一次,可以使用以下命令:viewModel.getCustomer(customerId)
LaunchedEffect(Unit){ // this it will execute just one
viewModel.getCustomer(customerId)
}
或者你可以直接从 ViewModel 中获取导航参数
class CustomerViewModel(private val savedStateHandle: SavedStateHandle) :
ViewModel(){
private val customerId = savedStateHandle.getLong(....)
init{
getCustomer(customerId )
}
您可以与 一起使用。参考我之前的回答by
LaunchedEffect
一些提示:
为了避免不必要的重构,将这些函数转换为使用函数引用,可以直接从 .这是你如何做到的:viewModel
addCustomer = viewModel::addCustomer,
updateCustomer = viewModel::updateCustomer
使用 Kotlin 在 Android 中处理共享状态时,请利用 StateFlow 来观察值的变化。请参阅 Android 参考
// Backing property to avoid state updates from other classes
private val _existingCustomer = MutableStateFlow<Customer?>(null)
// The UI collects from this StateFlow to get its state updates
val existingCustomer: StateFlow<Customer?> = _existingCustomer
评论