Quick React 4 Hooks State Machine
Posted on October 2, 2021
Tags: javascript
1 summary
| Concept | Producer | Consumer | Description |
|---|---|---|---|
| State | setState | getState | Obvious |
| Context | const MyContext = createContext(null) |
<MyContext.Provider ...> |
Only by creating the Context can we create the Context Provider JSX element |
| Context | <MyContext.Provider value={}/> specifically the value |
const myService = useContext(MyContext) |
Only by setting the value in the Context Provider, are we allowed to use the value via useContext (THIS GETS ENHANCED BY XSTATE useActor) |
| Context | setState(stuff) |
<MyContext.Provider value={{getState()}}/> |
context’s value is both a producer and a consumer. (THIS GETS OBSOLETED BY XSTATE useInterpret) |
| Concept | Producer | Consumer | Description |
|---|---|---|---|
| XState | const authService = useInterpret(authMachine); |
<MyContext.Provider value={{authService}}/> |
useInterpret is the alternative to <MyContext.Provider value={{getState()}} since it prevents needless rerenders |
| XState | const myService = useContext(MyContext) |
const [bleh] = useActor(myService.data) |
enchanced from useContext |
2 xState keywords
- INTERFACE: xState
createMachinebuilds a statemachine which serves as an interface.
- (React-Only) IMPLEMENTATION:
useMachine(someMachine,{actions:{..}})hook provides the implementation mainly theactionswhich causes the side-effects in our application. - Remember you MUST
.start()your machine at some point beforesend()or anything - to query your machine current states do
YourMachine.getSnapshot().toString()
import { Machine, assign, createMachine, interpret, } from "xstate";
const QueueMachine = createMachine(
{
/** @xstate-layout N4IgpgJg5mDOIC5QEUCuZ0DoCyB5AcrgJIAiAxAAoCqAygBIDaADALqKgAOA9rAJYAuvLgDt2IAB6IAtAGYArE0wAOJgCZVAdgAsSnTIBscgIwAaEAE9ERo1sxHVTfUofqmBtQF8PZtBjA4CYnIKXApmNiQQbj5BETFJBFkFZTVNHT1DUwtEVSMlZTklfXkNVSV5GS8fdCw8QlJMQnwAUWwKABUATTJwsWiBIVFIhKYzSwQmKpBfWsCG1o7u3sj+2KHQBKkjRS0ZbQBODW3cpX2VfbGrff3MLTUZXZkZazkZMq9vEGEuCDgxGbAfR4Aziw2kMjOKXU2l05Uyl0SRm0mGuqP2TAxckOGg0UwBAXqJCBMUG8WkNiMmAxTCR9n09O0GgRUiUGluaP2CjUZyUSjxNX8dSCjQICy6xJB6wkViRmAccgUr30THSWiZ2QQ9nyuS0r1Uh3puyYfM++KF8za4pWwLWZMS+xkVIxtNU9P0jIR1huWn2RkK5TKNNKHw8QA */
id: "Queue",
predictableActionArguments: true,
initial: "MONOID",
context: {
count: 0,
data: [],
},
states:{
MONOID : {
initial: "EMPTY",
states: {
NONEMPTY:{
always:[{
target: "EMPTY",
cond: "isEmptyCond",
actions: (context,event) => console.log("rfe")}]
},
EMPTY:{
always:[{
target: "NONEMPTY",
cond: "isNotEmptyCond",
actions: (context,event) => console.log("ahh")}]
}
},
on: {
PUSH: {
target: "MONOID",
internal: true,
// actions: (context,event) => assign({count: context.count + 1})
actions: assign({
count: (context,event) => {return context.count + 1; },
data: (context,event) => {return context.data.concat(event.data);},
})
},
POP: {
target: "MONOID",
internal: true,
actions: assign({
count: (context,event) => {return context.count - 1},
data: (context,event) => {return context.data.slice(1)},
})
}
}
}
}},
{
guards: {
isEmptyCond: (context,event)=> {return (context.count == 0)},
isNotEmptyCond: (context,event)=> {return (context.count != 0)}
}
}
)
class MyQueue{
bleh = interpret(QueueMachine);
constructor(myarray){
this.bleh.onTransition((state) => console.log(state.value)).start()
myarray.forEach(element => {
this.bleh.send("PUSH",{data: element});
});
};
push(newdata){
this.bleh.send("PUSH",{data: newdata})
return this.bleh.getSnapshot().context.data
}
pop(){
if(this.bleh.getSnapshot().matches({"MONOID": "NONEMPTY"})){
let toReturn = this.bleh.getSnapshot().context.data[0];
this.bleh.send("POP")
return toReturn
}
else if(this.bleh.getSnapshot().matches({"MONOID": "EMPTY"})){
return null
}
}
len(){
return this.bleh.getSnapshot().apply().context.count
}
log(){
console.log(this.bleh.getSnapshot().context, this.bleh.getSnapshot().toStrings())
return
}
}
// bleh.send("PUSH",{data: "shit"})
// bleh.send("PUSH",{data: "crap"})
// bleh.send("POP")
// bleh.send("isEmpty")
// bleh.send("isEmpty")
// console.log(bleh.getSnapshot().nextEvents)
// console.log(bleh.getSnapshot().context)
const bbb = new MyQueue([])
bbb.push(3)
// console.log(bbb.bleh.getSnapshot().context)
bbb.push(5)
bbb.pop()
bbb.pop()
console.log(bbb.bleh.getSnapshot().matches({MONOID: "EMPTY"}))
console.log(bbb.pop())
console.log(bbb.pop())
bbb.log()
// console.log(bbb.bleh.getSnapshot().nextEvents)
// const vvv = new MyQueue([3,5,6,7]);
// console.log(bbb.pop())
// console.log(bbb.push(95))
// console.log(bbb.pop())id: 'bleh',
initial: 'hereitbegins',
context: {
GLOBALSTUFF: undefined,
MYCONFIGS: {},
},
states: {
}3 useMemo
import "./styles.css";
import React, {useMemo,useState,useEffect} from 'react';
const slowfunction = (num) => {
console.log("slow function");
for(let i = 0 ; i <= 10000000; i++){};
return num * 2;
}
export default function App() {
const [count,setCount] = useState(0);
const [count2,setCount2] = useState(0);
const memoizedslowfunction = useMemo(() => {
return slowfunction(count)
},[count]);
return (
<div className="App">
<button onClick = {event => {
setCount(cstate => cstate + 1)
}}>Not Often pressed button</button>
<p>Not Often Updated Value: {slowfunction(count)}</p>
<p>Above count value needs to be memoized</p>
<h2>----------</h2>
<button onClick = {event => {
setCount2(cstate => cstate + 1)
}}>Often pressed button</button>
<p>Often Updated Value: {count2}</p>
</div>
);
}4 Adapting external libraries into JS
- Typical external libraries will act upon a DOM element like.
new Sigma(document.getElementById("mydiv")",mygraph,settings)
const mygraph = new Graph({multi: true});
mygraph.import(graphdata)
const settingsSigma = { labelRenderedSizeThreshold: 1,}
const mydiv = document.getElementById("mydiv");
new Sigma(mydiv,mygraph,settings);const MyComponent = () => {
const myDiv = useRef(null);
let hoveredEdge = null;
useEffect(()=>{
const mygraph = new Graph({multi: true});
mygraph.import(graphdata)
const settingsSigma = { labelRenderedSizeThreshold: 1,}
const renderer = new Sigma(mygraph,myDiv.current,settingsSigma);
return () => {//disposal - or else if you resize, you get duplicate graphs
renderer.kill()
}
},[])
return(
<>
<div ref={myDiv} style={{"height" : "100vh", "width" : "50%"}}></div>
</>
)
}