mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
2bc8ce1549
Summary: Changelog: [internal] # What is Teller? Teller is a bank's employee who deals with the customer on behalf of the bank. In Fabric's scenario it is a class that on behalf of the view deals with State. # Why do we need it? Dealing with `ConcreteState` can be complicated and patterns are often repeated among different component views. `ConcreteStateTeller` aims to resolve these issues. Examples: - You can call teller's methods without checking for nullptr (we have had crashes because of this before). - Methods are save to be called on any thread. - Mechanism to retry state update if it fails is built in. It is designed to be used from ComponentView so views don't have to talk directly to `ConcreteState`. Reviewed By: JoshuaGross, shergin Differential Revision: D23216865 fbshipit-source-id: 90a50702e036eac084f89743ebab687a67182dc0
153 lines
4.0 KiB
C++
153 lines
4.0 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <better/optional.h>
|
|
#include <glog/logging.h>
|
|
#include <react/renderer/core/ConcreteState.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
/*
|
|
* Wrapper for `ConreteState` class designed to make interactions with
|
|
* ConcreteState easier.
|
|
*/
|
|
template <typename ConcreteStateT>
|
|
class ConcreteStateTeller {
|
|
public:
|
|
using Data = typename ConcreteStateT::Data;
|
|
|
|
/*
|
|
* Sets backing `ConcreteState` on which all the methods will be called.
|
|
* Can be called from any thread.
|
|
*/
|
|
void setConcreteState(State::Shared const &state) {
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
concreteState_ = std::static_pointer_cast<ConcreteStateT const>(state);
|
|
}
|
|
|
|
/*
|
|
* Removes reference to `ConcreteState` previously set in `setConcreteState`.
|
|
* Can be called from any thread.
|
|
*/
|
|
void invalidate() {
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
concreteState_ = nullptr;
|
|
}
|
|
|
|
/*
|
|
* Returns data if state isn't nullptr.
|
|
* Can be called from any thread.
|
|
*/
|
|
better::optional<Data> getData() const {
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
if (concreteState_) {
|
|
return concreteState_->getData();
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns true if backing state isn't nullptr, false otherwise.
|
|
* Can be called from any thread.
|
|
*/
|
|
bool isValid() const {
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
return concreteState_ != nullptr;
|
|
}
|
|
|
|
/*
|
|
* Initiate a state update process with given new data and priority.
|
|
* This is a simplified convenience version of the method that receives a
|
|
* function for cases where a new value of data does not depend on an old
|
|
* value.
|
|
*/
|
|
void updateState(
|
|
Data &&newData,
|
|
EventPriority priority = EventPriority::AsynchronousUnbatched) const {
|
|
updateState(
|
|
[data = std::move(newData)](Data const &oldData) -> Data {
|
|
return std::move(data);
|
|
},
|
|
priority);
|
|
}
|
|
|
|
/*
|
|
* Initiate a state update process with a given function (that transforms an
|
|
* old data value to a new one) and priority. The update function can be
|
|
* called from any thread any moment later. The function can be called only
|
|
* once or not called at all (in the case where the node was already unmounted
|
|
* and updating makes no sense). The state update operation might fail in case
|
|
* of conflict.
|
|
*/
|
|
void updateState(
|
|
std::function<Data(Data const &oldData)> callback,
|
|
EventPriority priority = EventPriority::AsynchronousBatched) const {
|
|
std::shared_ptr<ConcreteStateT const> concreteState;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
if (!concreteState_) {
|
|
return;
|
|
}
|
|
concreteState = concreteState_;
|
|
}
|
|
|
|
concreteState->updateState(
|
|
callback,
|
|
[=]() {
|
|
updateStateRetryIfNecesarry_(concreteState, callback, priority, 1);
|
|
},
|
|
priority);
|
|
}
|
|
|
|
private:
|
|
/*
|
|
* Protected by `mutex_`.
|
|
*/
|
|
std::shared_ptr<ConcreteStateT const> concreteState_;
|
|
|
|
/*
|
|
* Protects `concreteState_`.
|
|
*/
|
|
std::mutex mutable mutex_;
|
|
|
|
void updateStateRetryIfNecesarry_(
|
|
std::shared_ptr<ConcreteStateT const> concreteState,
|
|
std::function<Data(Data const &oldData)> callback,
|
|
EventPriority priority,
|
|
int retryCount) const {
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
|
|
if (concreteState != concreteState_) {
|
|
LOG(WARNING) << "ConcreteState_ changed while retrying";
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (retryCount > 60) {
|
|
LOG(ERROR) << "Exceeded 60 retries";
|
|
assert(false);
|
|
return;
|
|
}
|
|
|
|
concreteState->updateState(
|
|
callback,
|
|
[=] {
|
|
updateStateRetryIfNecesarry_(
|
|
concreteState, callback, priority, retryCount + 1);
|
|
},
|
|
priority);
|
|
}
|
|
};
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|