Ansätze zum State Sharing: Ember
Zuvor habe ich über die Zustandsfreigabe in React geschrieben . Ich werde in diesem Artikel oft auf Teile dieses Beitrags verweisen, um Ansätze in Ember zu vergleichen.
Modelle
In React liegen die meisten Verantwortlichkeiten bei der Komponente – Datenladen, Zustandsverwaltung, Geschäftslogik und Template-Rendering. Selbst in einem Framework wie Next.js dienen Konstrukte wie Pages kaum mehr als der Organisation von Komponenten unter verschiedenen Pfaden. Im Gegensatz dazu schreibt Ember einen Teil der oben genannten Verantwortlichkeiten über Routen, Controller, Vorlagen und, was für unsere Diskussion am wichtigsten ist, Modelle vor.
In Ember kann das eingebaute Modellkonstrukt einen Großteil der schweren Arbeit in Bezug auf das Bohren von Stützen übernehmen. Ähnlich wie bei der Komponentenzusammensetzung oder Dispatches in React können Modelle mehrere Variablen einschließen und transportieren und viele Komponenten abschließen.
Ein wesentlicher Unterschied besteht darin, dass Ember vorschreibt, dass Modelle in Routen* geladen werden, die immer die Wurzel eines Komponentenbaums sind. Dies steht im Gegensatz zu der Flexibilität, Daten an jedem Knoten in React abzurufen. Sie können Modelle entweder über datenbankgestützte Aufrufe an den Ember-Datenspeicher laden oder Nur-Speicher-Modellobjekte instanziieren. In jedem Fall fügen Sie ein Modell in eine Route und ihren Unterbaum ein, indem Sie es in den modelHook der Route zurückgeben. Danach kann auf das Modell in Routenvorlagen über das @modelSchlüsselwort zugegriffen und durch Komponenten weitergegeben werden.
// routes/shop.js
import Route from '@ember/routing/route';
import { service } from "@ember/service";
export const ShopRoute extends Route {
@service store;
// memory-only (until persisted to database)
async model() {
return this.store.createRecord('shop', {
sales: 0,
customers: [
{
id: 1,
name: 'bob',
},
{
id: 2,
name: 'susan',
}
],
});
}
// OR load existing from database
export const ShopRoute extends Route {
@service store;
async model() {
return this.store.findRecord('shop', 'id123abcd');
}
}
// templates/shop.hbs
<h4> Number of sales: {{@model.sales}} </h4>
Hier ist die vollständige Routenvorlage, die Modelldefinition und die untergeordnete Komponente, um das Beispiel zu vervollständigen:
// models/shop.js
export default class ShopModel extends Model {
@attr('number', { defaultValue: 0 }) sales;
@attr customers;
addSale() {
this.sales += 1;
}
}
// templates/shop.hbs
Number of sales: {{@model.sales}}
<CustomerList @shop={{@model}} />
// components/customer-list.hbs
{{#each @shop.customers as |customer|}}
<CustomerPanel @shop={{@shop}} />
{{/each}}
Heben von Komponenten
Erinnern Sie sich daran, dass im Abschnitt „Component Hoisting“ meines React-Artikels Blattkomponentendefinitionen bis zu einer Ancestor-Komponente „angehoben“ werden, damit sie auf den Ancestor-Zustand zugreifen können. React nennt dies „Komponentenzusammensetzung“. Währenddessen können wir in Ember keine Komponenten innerhalb anderer Komponenten definieren. Es gibt eine klare Trennung von Komponenten nach Dateien sowie eine Trennung von Vorlagen und JavaScript-Komponentenlogik.
Wir haben zwei Möglichkeiten, die Komponentenzusammensetzung von React nachzuahmen und Komponenten bis zu einem Vorfahren zu „heben“.
Die erste besteht darin, Embers componentHelfer zu verwenden. Dies ermöglicht dem Vorfahren, eine Komponente als Argument an untergeordnete Komponenten weiterzugeben und das Rendern auf ihre Blattposition zu verschieben.
// component-d.hbs
{{#each @shop.customers as |customer|}}
<div>
{{customer.name}}
<button onClick={{fn @deleteCustomer customer.id}}>
</div>
))
<button onClick={{fn @addCustomer customer.id}}> Add Customer </button>
{{/each}}
// parent.hbs
<IntermediateB
listCustomers={{component 'component-d' shop=@model addCustomer=this.addCustomer deleteCustomer=this.deleteCustomer}}
/>
// intermediate-b.hbs
<IntermediateC
listCustomers={{@listCustomers}}
/>
// intermediate-c.hbs
<div>
Customer List
<@listCustomers />
</div>
Anstatt die Blattknotendefinition aufzurufen, können wir alternativ Zwischenknoten haben, die den Nachkommen "offenlegen", der auf den Vorfahrenzustand zugreifen muss. Dazu nutzen wir die yieldFunktion von Ember.
Wie bei React props.childrenkönnen Sie in Ember Elemente als Argumente über die Blockform der Komponente übergeben, indem Sie verwenden {{yield}}. Ember's yieldgeht jedoch noch einen Schritt weiter, indem es Komponenten erlaubt, über die Blockform auch Wahlinterna offenzulegen.
// component-d.hbs
{{#each @shop.customers as |customer|}}
<div>
{{customer.name}}
<button onClick={{fn @deleteCustomer customer.id}}>
</div>
))
<button onClick={{fn @addCustomer customer.id}}> Add Customer </button>
{{/each}}
// parent.hbs
<IntermediateB as |interB|>
<interB.listCustomers
@shop={{this.shop}}
@addCustomer={{this.addCustomer}}
@deleteCustomer={{this.deleteCustomer}}
/>
</IntermediateB>
// intermediate-b.hbs
<IntermdiateC as |interC|>
{{yield (hash
listCustomers=interC.componentD))
)}}
</IntermediateC>
// intermediate-c.hbs
<div>
Customer List
{{yield (hash
listCustomers=(component 'component-d'))
)}}
</div>
Dienstleistungen
Services sind eine „globalere“ Version von React Contexts. Sie können Dienste über viele Konstrukte hinweg einfügen, darunter Routen, Controller, Komponenten und sogar Modelle. Sie enthalten Zustand und Abschlüsse, die mit jedem verbrauchenden Konstrukt geteilt werden.
Im Gegensatz zu Kontexten gibt es keine Möglichkeit, Dienste bestimmten Teilbäumen zuzuordnen. Ein Dienst kann in jeden Knoten injiziert werden. Ember empfiehlt, Dienste sparsam zu verwenden, um wirklich standortweite Funktionen wie Einkaufswagen oder Authentifizierung zu erhalten.
// services/shop.js
export default class ShopService extends Service {
// lazy-loaded
shop = this.store.createRecord('shop', {
sales: 0,
customers: [],
});
addSale() {
if(shop) { // ensure shop is loaded
this.shop.sales += 1;
this.shop.save();
}
}
}
// components/customer-list.js
export default class CustomerListComponent extends Component {
@service
shop;
get customers() {
return this.shop.customers;
}
@action
updateSales() {
this.shop.addSale();
}
}
URL-Parameter
Die Idee hier ist genau die gleiche wie im jeweiligen React-Abschnitt, nur die Code-Implementierung ist anders. Dynamische Segmente werden in der Router-Datei definiert, und Abfrageparameter werden in Controllern definiert.
// router.js
// generates routes for
// shop/:id
// shop/:id/activity
// - e.g. shop/123/refunds
// shop/123/purchases
this.route('shop', { path: 'shop/:shopid' }, function () {
this.route('activities', '/:activity');
});
// controllers/shop/activity.js
export default class ShopActivityController extends Controller {
queryParams = [
'showShoppingCartSidebar',
'isDarkMode'
];
Wie in React können Sie hier die Webspeicher-API des Browsers und insbesondere die Bereitstellung von localStorage verwenden. Es ist nichts Besonderes, es in Ember zu verwenden. Sie können sich dafür entscheiden, ein lokales Speicher-Add-on wie ember-local-storage für eine API auf höherer Ebene zu verwenden, die von Ember berechnete Eigenschaften so umwandelt, dass sie automatisch durch lokalen Speicher gesichert werden, und dauerhafte Daten für Sie verarbeitet. Oder Sie können Lese- und Schreibvorgänge in den lokalen Speicher schreiben. Hier ist ein Beispiel in einer Komponente:
// components/dark-mode-toggle.js
export default class DarkModeToggleComponent extends Component {
get isDarkMode() {
return localStorage.getItem('myApp_isDarkMode') || false;
}
@action
toggleDarkMode() {
localStorage.setItem(!this.isDarkMode);
}
}
Wenn Sie Ember vollständig übernehmen, werden Sie Modelle stark nutzen und viel objektorientierte Programmierung durchführen. Dies könnte im Gegensatz zum modernen React stehen, das in Richtung funktionale Programmierung tendiert. Modelle können verwendet werden, um eine große Anzahl von Zuständen tief in einen Komponenten-Unterbaum zu „tragen“. Dies ist vergleichbar mit der Verwendung von Dispatches und einer Komponentenzusammensetzung in React.
Komponentenzusammenstellung – also „Component Hoisting“, wie ich es gerne nenne – kann auch in Ember passieren. Es sieht ein wenig anders aus, da es Embers yieldKonstrukt und den componentTemplate-Helfer verwendet.
Darüber hinaus sind Ember Services wie React Contexts, da sie dazu beitragen, Prop Drilling vollständig zu vermeiden. Dienste sind globaler als Kontexte und können nicht auf bestimmte Teilbäume beschränkt werden.
Schließlich können die Ansätze URL-Parameter und Local Storage auch in Ember verwendet werden. Sie unterscheiden sich nur geringfügig von React in ihrer Code-Implementierung.
Damit ist mein Diptychon zu Ansätzen zum State Sharing abgeschlossen. Hoffe es hat euch gefallen!
Habe ich etwas übersehen oder einen Fehler in meinem Pseudocode entdeckt? Haben Sie Feedback? Hinterlasse unten einen Kommentar!

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































