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
createMachine
builds a statemachine which serves as an interface.
- (React-Only) IMPLEMENTATION:
useMachine(someMachine,{actions:{..}})
hook provides the implementation mainly theactions
which 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{
= interpret(QueueMachine);
bleh constructor(myarray){
this.bleh.onTransition((state) => console.log(state.value)).start()
.forEach(element => {
myarraythis.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([])
.push(3)
bbb// console.log(bbb.bleh.getSnapshot().context)
.push(5)
bbb.pop()
bbb.pop()
bbbconsole.log(bbb.bleh.getSnapshot().matches({MONOID: "EMPTY"}))
console.log(bbb.pop())
console.log(bbb.pop())
.log()
bbb// 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())
: 'bleh',
id: 'hereitbegins',
initial: {
contextGLOBALSTUFF: 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});
.import(graphdata)
mygraphconst 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});
.import(graphdata)
mygraphconst settingsSigma = { labelRenderedSizeThreshold: 1,}
const renderer = new Sigma(mygraph,myDiv.current,settingsSigma);
return () => {//disposal - or else if you resize, you get duplicate graphs
.kill()
renderer
},[])
}return(
<>
<div ref={myDiv} style={{"height" : "100vh", "width" : "50%"}}></div>
</>
) }