11package react
22
33import (
4+ "fmt"
5+
46 "github.com/gopherjs/gopherjs/js"
57)
68
@@ -28,16 +30,24 @@ type (
2830
2931 // State is a value managed by React's useState hook.
3032 // If the component function hasn't been called yet,
31- // Get returns zero and Set has no effect, otherwise
32- // it will return the current value and update it respectively.
33- // NOTE: `State[int]` should be `State[float64]` because of
34- // how the underlying React hook works.
33+ // the initial value is used and updated until the component is rendered.
34+ // After the component is rendered, the value is managed by React.
3535 // See: https://react.dev/reference/react/useState
36+ //
37+ // NOTE: Some types like `State[int]` must instead be `State[float64]`,
38+ // and `State[[]*foo]` must instead be `State[[]any]`, because of
39+ // how the underlying React hook works.
3640 State [T any ] struct {
3741 initial T
3842 getter , setter * js.Object
3943 }
4044
45+ // Ref is a React ref created with UseRef().
46+ // It is a mutable object with a `current` property that can hold any value.
47+ // Modifying the `current` property does not trigger re-renders.
48+ // See: https://react.dev/reference/react/useRef
49+ Ref struct { holder * js.Object }
50+
4151 // ComponentFunc is a function that defines a React component.
4252 // It takes props as input and returns a React element.
4353 // See: https://react.dev/learn
@@ -47,6 +57,10 @@ type (
4757 // It is an interface that exposes a Render methods with the same signature
4858 // as a ComponentFunc so that it can be used simular to a React component.
4959 Component interface { Render (props Props ) * Element }
60+
61+ // Root is a React root created with CreateRoot().
62+ // It is used to render React nodes into the DOM.
63+ // See: https://react.dev/reference/react-dom/client/createRoot
5064)
5165
5266func reactDom () * js.Object { return js .Global .Get (`ReactDOM` ) }
@@ -114,40 +128,80 @@ func Button(value string, props Props, onClick func(e *js.Object)) *Element {
114128 return CreateElement (`input` , props )
115129}
116130
131+ // NewState creates a new State with the given initial value.
132+ // The initial value is only used before the component function is called
133+ // for the first time. After that, the value is managed by React.
134+ // See: https://react.dev/reference/react/useState
117135func NewState [T any ](initial T ) * State [T ] {
136+ fmt .Printf ("NewState called [%T]\n " , initial ) // TODO(grantnelson-wf): REMOVE
118137 return & State [T ]{initial : initial }
119138}
120139
140+ // Use initializes the state for the current component render.
141+ // It must be called unconditionally at the top level of the component function.
142+ // This takes the place of calling React's UseState so that the state can be
143+ // created outside of the component function.
144+ // See: https://react.dev/reference/react/useState
121145func (s * State [T ]) Use () {
146+ fmt .Printf ("Use called [%T]\n " , s .initial ) // TODO(grantnelson-wf): REMOVE
122147 r := react ().Call (`useState` , s .initial )
123148 s .getter = r .Index (0 )
124149 s .setter = r .Index (1 )
125150}
126151
127- func (s * State [T ]) Get () ( v T ) {
152+ func (s * State [T ]) Get () T {
128153 if s != nil {
129154 if s .getter != nil {
155+ fmt .Printf ("Get called via getter [%T]\n " , s .initial ) // TODO(grantnelson-wf): REMOVE
130156 return s .getter .Interface ().(T )
131157 }
158+ fmt .Printf ("Get called without getter [%T]\n " , s .initial ) // TODO(grantnelson-wf): REMOVE
132159 return s .initial
133160 }
134- return
161+ var zero T
162+ fmt .Printf ("Get called on nil [%T]\n " , zero ) // TODO(grantnelson-wf): REMOVE
163+ return zero
135164}
136165
137166func (s * State [T ]) Set (v T ) {
138167 if s != nil {
139168 if s .setter != nil {
169+ fmt .Printf ("Set called with setter [%T]\n " , s .initial ) // TODO(grantnelson-wf): REMOVE
140170 s .setter .Invoke (v )
141171 return
142172 }
173+ fmt .Printf ("Set called without setter [%T]\n " , s .initial ) // TODO(grantnelson-wf): REMOVE
143174 s .initial = v
144175 }
145176}
146177
147- func UseRef () * js.Object {
148- return react ().Call (`useRef` , nil )
178+ // UseRef creates a mutable ref object that persists for the lifetime of the component.
179+ // The ref object has a `current` property that can hold any value.
180+ // See: https://react.dev/reference/react/useRef
181+ func UseRef () * Ref {
182+ return & Ref {holder : react ().Call (`useRef` , nil )}
183+ }
184+
185+ func (r * Ref ) Current () * js.Object {
186+ return r .holder .Get (`current` )
187+ }
188+
189+ func (r * Ref ) Get (key string ) * js.Object {
190+ return r .Current ().Get (key )
191+ }
192+
193+ func (r * Ref ) Set (key string , value any ) {
194+ r .Current ().Set (key , value )
195+ }
196+
197+ func (r * Ref ) Call (name string , args ... any ) {
198+ r .Current ().Call (name , args ... )
149199}
150200
201+ // UseEffect registers an effect function that is called after rendering.
202+ // The effect is re-run whenever any of the dependencies change.
203+ // If no dependencies are provided, the effect is only run once after the initial render.
204+ // See: https://react.dev/reference/react/useEffect
151205func UseEffect (effect func (), deps ... any ) {
152206 react ().Call (`useEffect` , effect , deps )
153207}
0 commit comments