Wenn man sich generische Componenten in React oder Vue bastelt, wie Lists oder Grids/Tables kommt man schnell zu dem Punkt, wo man Werte aus einem Object extrahieren muss. Z. B. wenn man sich in einer Table eine Column definiert. Man könnte eine function rein reichen, die auf die konkrete Implementierung des Item zugeschnitten ist, aber oft will man nur die vorhandenen Werte im Object zur Anzeige bringen und nicht zu viel schreiben.
Wenn man einen Key hat und auf der ersten/obersten Ebene agiert wie bei item.id ist alles einfach. Bei item.sub.moreSub.verySub.id ist es schon schwieriger.. naja eigentlich nicht und jeder hat so etwas schon mal gebastelt. Hier meine Lösung die vielleicht jemanden 5 Minuten Arbeit ersparen kann :-)
function getObjectValue(obj, path){
let curObj = obj;
const parts = path.split(".");
for(let i = 0; i < parts.length; i++){
if(curOjb[parts] !== undefinded){
curObj = curObj[parts];
}
else {
return undefined;
}
}
return curObj;
}
getObjectValue({sub:{id: 23}}, "sub.id") == 23
Manchmal hat man bei React trotz besserem Wissens sich Strukturen gebaut, die es schwierig machen, den Datenfluss sauber abzubilden. So passierte es plötzlich, dass in einer Child-Component etwas geändert wurde und diese Änderung es verlangte, dass in einer Geschwister Child-Component eine Liste neu geladen werden sollte. Diese Component lud die Liste von einer REST-API selber und bekam die Liste nicht von der Parent-Component. Doof. Per Callback wurde die Parent-Component von der Änderung unterrichtet, aber diese konnte das Neuladen ja nicht so einfach anstoßen, weil es an sich keine State-Änderung gab.
Man braucht ein Flag, dass sich ändert, wenn ein Refresh der Child-Component nötig wurde. Da reicht ein bool-Flag, das bei jeder Änderung seinen Zustand ändert.
Hier wird es einmal sehr gut erklärt, aber mit den neuen Hooks von React geht es sehr elegant umzusetzen.
Hier der Code aus der Parent-Component:
const[trigger,setTrigger] = useState(false);
return (
<>
<child1 onSubmit={() => {setTrigger(!trigger)}}/>
<child2 refresh={trigger}/>
</>
);
Damit child2 nun was laden kann muss man nicht mehr groß selbst prev-props mit next-props vergleichen sondern kann useEffect() verwenden:
useEffect(() => {reloadList()}, props.refresh);
Schön einfach und kompakt. Das man mit useEffect() direkt auf einzelne props/Änderungen reagieren kann, macht mir React schon sehr viel sympathischer. Erinnert mich an Watcher und Proxies aus Vue, nur sehr viel einfacher zu verwenden.
Heute saß ich vor der Dokumentation zu den neuen Hooks bei React und las mit durch welche Probleme die alle lösen sollen. Indirekt wurde gesagt, dass sie Redux unnötig machen sollen.. also war für mich die Zeit gekommen mir wirklich mal Redux anzusehen. Erstmal zu verstehen, was es tut, ist nicht ganz so einfach, wenn man sich deren Doku durchliest. Was gefühlt aber nur an deren Doku liegt, die viel zu zerstückelt ist, um einen schnellen Überblick zu bekommen.
Ich habe mit Hilfe von anderen Seiten dann heraus bekommen. Dass Redux einen State pro Reducer hält und der alle Actions bekommt und dann mit eigener Logik, die für ihn interessanten Actions ab arbeitet. Alter State geht rein und neuer geht raus. Eine Action hat einen Typ und ein Payload. Wenn sich der State ändert, wird etwas getriggert. Aber ich fand, dass doch für einfache Beispiele zu viel Code da war um es zu verstehen.
Um also das Prinzip von Redux zu verstehen habe ich dann mal 20min investiert und mir ein ähnliches Kontrukt geschaffen. Etwas Topic orientierter mit den States, so dass man die Reducer pro Action hat, aber vom Prinzip ist es alles relativ einfach und das Bootstrapping wurde daruch auch übersichtlicher.
Die meisten Beispiele zu Redux bestehen sowie so zu 50% aus React. Aber ich wollte ja Redux verstehen und nicht Redux+React (ja.. die gibt es wirklich auf getrennt von einander!)
Hier das Ergebnis meines Selbstversuches:
const StateStore = {
states: {},
actions: {},
createState: function(name){
this.states[name] = {
data: null,
workers: [],
listeners: []
}
},
registerWorker: function(statename, action, worker){
if(this.states[statename]){
if(!this.states[statename].workers[action]){
this.states[statename].workers[action] = []
}
this.states[statename].workers[action].push(worker);
if(!this.actions[action]){
this.actions[action] = [];
}
this.actions[action].push(statename);
}
},
registerListener: function(statename, listener){
if(this.states[statename]){
this.states[statename].listeners.push(listener);
}
},
action: function(action, payload){
if(this.actions[action]){
this.actions[action].forEach((statename) => {
let changes = false;
this.states[statename].workers[action].forEach((worker) => {
const stateCopy = JSON.parse(JSON.stringify(this.states[statename].data));
let newState = worker(this.states[statename].data, payload);
if(newState != stateCopy){
changes = true;
}
this.states[statename].data = newState;
});
if(changes){
this.states[statename].listeners.forEach((func) => {func(this.states[statename].data)});
}
});
}
}
};
Dazu mein kleiner Test:
StateStore.createState("test");
StateStore.registerWorker("test", "inc", (state, payload) => {
if(state === null){
return 0;
}
else {
return ++state;
}
});
StateStore.registerWorker("test", "dec", (state, payload) => {
if(state === null){
return 0;
}
else {
return --state;
}
});
StateStore.registerWorker("test", "dn", (state, payload) => {
console.log("(" + state + ") do nothing");
//change-listeners are not triggert
return state;
});
StateStore.registerListener("test", (state) => {console.log(state)});
StateStore.action("inc", null); //0
StateStore.action("inc", null); //1
StateStore.action("inc", null); //2
StateStore.action("dn", null); //(2) do nothing
StateStore.action("inc", null); //3
StateStore.action("dec", null); //2
Lief erstaunlicher Weise direkt wie es sollte (nach dem ein kleiner Fehler bei einer falschen Variablen behoben war).
Damit sollte man durch aus das gesamte Statemanagement einer Componente abbilden können. Wenn man nun noch Events verwenden würde, geht es schon stark in Richtung Reactive-Programming.
https://github.com/annonyme/jsstatestore