mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Introduce ConcreteStateTeller
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
This commit is contained in:
committed by
Facebook GitHub Bot
parent
549563e8ab
commit
2bc8ce1549
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <react/renderer/core/ConcreteState.h>
|
||||
#include <react/renderer/core/ConcreteStateTeller.h>
|
||||
#include <react/renderer/core/Props.h>
|
||||
#include <react/renderer/core/ShadowNode.h>
|
||||
#include <react/renderer/core/StateData.h>
|
||||
@@ -48,6 +49,7 @@ class ConcreteShadowNode : public BaseShadowNodeT {
|
||||
using SharedConcreteEventEmitter = std::shared_ptr<EventEmitterT const>;
|
||||
using SharedConcreteShadowNode = std::shared_ptr<ConcreteShadowNode const>;
|
||||
using ConcreteState = ConcreteState<StateDataT>;
|
||||
using ConcreteStateTeller = ConcreteStateTeller<ConcreteState>;
|
||||
using ConcreteStateData = StateDataT;
|
||||
|
||||
static ComponentName Name() {
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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
|
||||
Reference in New Issue
Block a user