|
1 | 1 | package react |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "errors" |
5 | | - "reflect" |
6 | | - |
7 | 4 | "github.com/gopherjs/gopherjs/js" |
8 | 5 | ) |
9 | 6 |
|
10 | 7 | type ( |
11 | | - // Component is a React component that can be rendered. |
12 | | - Component struct{ *js.Object } |
13 | | - |
14 | | - // State represents the state of a React component. |
15 | | - // It can be a map, a struct, or a pointer to a struct. |
16 | | - State any |
17 | | - |
18 | | - // Renderable is an interface for types that can render React nodes. |
19 | | - // This is used to let the Component call the Render method |
20 | | - // in the "extending" struct. |
21 | | - Renderable interface{ Render() Node } |
| 8 | + Component struct{ *js.Object } |
| 9 | + State map[string]any |
| 10 | + RenderFunc func(*Component) Node |
22 | 11 | ) |
23 | 12 |
|
24 | | -func NewComponent(props Props, r Renderable, initialState State) *Component { |
25 | | - c := &Component{Object: react().Get(`Component`).New(props)} |
26 | | - c.Set(`render`, func() Node { return r.Render() }) |
27 | | - c.Set(`state`, stateToMap(initialState)) |
28 | | - return c |
29 | | -} |
30 | | - |
31 | | -func (c *Component) Render() Node { |
32 | | - return c.Call(`render`) |
| 13 | +func NewComponent(props Props, init State, render RenderFunc) *Element { |
| 14 | + reactComp := react().Get("Component") |
| 15 | + ctor := js.MakeFunc(func(this *js.Object, args []*js.Object) any { |
| 16 | + reactComp.Call(`call`, props) |
| 17 | + this.Set(`state`, init) |
| 18 | + return nil |
| 19 | + }) |
| 20 | + rf := js.MakeFunc(func(this *js.Object, args []*js.Object) any { |
| 21 | + return render(&Component{Object: this}) |
| 22 | + }) |
| 23 | + ctor.Set(`prototype`, reactComp.Get(`prototype`)) |
| 24 | + ctor.Get(`prototype`).Set(`constructor`, ctor) |
| 25 | + ctor.Get("prototype").Set("render", rf) |
| 26 | + return &Element{Object: react().Call(`createElement`, ctor)} |
33 | 27 | } |
34 | 28 |
|
35 | | -func (c *Component) GetState(name string) *js.Object { |
36 | | - return c.Get(`state`).Get(`state`) |
| 29 | +func (c *Component) GetState(key string) *js.Object { |
| 30 | + return c.Get("state").Get(key) |
37 | 31 | } |
38 | 32 |
|
39 | | -func (c *Component) SetState(state State) { |
40 | | - c.Call(`setState`, stateToMap(state)) |
| 33 | +func (c *Component) SetState(newState State) { |
| 34 | + c.Call("setState", newState) |
41 | 35 | } |
42 | 36 |
|
43 | | -func stateToMap(v State) map[string]any { |
44 | | - if v == nil { |
45 | | - return nil |
46 | | - } |
47 | | - r := reflect.ValueOf(v) |
48 | | - if r.Kind() == reflect.Ptr { |
49 | | - r = r.Elem() |
50 | | - } |
51 | | - m := make(map[string]any) |
52 | | - switch r.Kind() { |
53 | | - case reflect.Map: |
54 | | - for _, key := range r.MapKeys() { |
55 | | - m[key.String()] = r.MapIndex(key).Interface() |
56 | | - } |
57 | | - case reflect.Struct: |
58 | | - t := r.Type() |
59 | | - for i := 0; i < r.NumField(); i++ { |
60 | | - name := t.Field(i).Name |
61 | | - if tag := t.Field(i).Tag.Get(`js`); tag != `` { |
62 | | - name = tag |
63 | | - } |
64 | | - m[name] = r.Field(i).Interface() |
65 | | - } |
66 | | - default: |
67 | | - panic(errors.New(`states must be a map, a struct, or a pointer to a struct`)) |
68 | | - } |
69 | | - return m |
| 37 | +//============================================ |
| 38 | + |
| 39 | +func NewCounter(initName string, initAge int) *Element { |
| 40 | + return NewComponent(Props{}, map[string]any{ |
| 41 | + `name`: initName, |
| 42 | + `age`: initAge, |
| 43 | + }, func(c *Component) Node { |
| 44 | + return Fragment( |
| 45 | + CreateElement(`input`, Props{ |
| 46 | + `value`: c.GetState(`name`).String(), |
| 47 | + `onChange`: func(e *js.Object) { |
| 48 | + c.SetState(State{`name`: e.Get(`target`).Get(`value`).String()}) |
| 49 | + }, |
| 50 | + }), |
| 51 | + CreateElement(`input`, Props{ |
| 52 | + `type`: `button`, |
| 53 | + `value`: `Increment age`, |
| 54 | + `onClick`: func() { |
| 55 | + c.SetState(State{`age`: c.GetState(`age`).Int() + 1}) |
| 56 | + }, |
| 57 | + }), |
| 58 | + CreateElement(`p`, nil, |
| 59 | + `Hello, `, c.GetState(`name`).String(), `. You are `, c.GetState(`age`).Int(), `.`), |
| 60 | + ) |
| 61 | + }) |
70 | 62 | } |
0 commit comments