Skip to content

Commit

Permalink
- added componentDidCatch method
Browse files Browse the repository at this point in the history
- added CreateContext and CloneElement functions
- Renamed SetInnerHTMLFunc and SetInnerHTML to have "Dangerously" prefix
- Use jsObjectIsNotNil function instead of jsObjectIsFunction to prevent bugs from Ref field in elements package
-
  • Loading branch information
rocketlaunchr-cto committed Oct 17, 2018
1 parent dc2e2df commit 0bb1f39
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 32 deletions.
2 changes: 1 addition & 1 deletion examples/uptime/timer_def.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
var TimerComponent *js.Object

type TimerProps struct {
// We will pass in the time that component was first instantiated.
// We will pass in the time when the component was first instantiated.
StartTime int64 `react:"start"`
}

Expand Down
31 changes: 31 additions & 0 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,34 @@ func ForwardRef(component interface{}) *js.Object {
}
})
}

// CreateContext is used when you want to pass data to a deeply
// embedded child component without using props.
// See: https://reactjs.org/docs/context.html#reactcreatecontext
func CreateContext(defaultValue ...interface{}) (Provider *js.Object, Consumer *js.Object) {

var res *js.Object

if len(defaultValue) > 0 {
res = React.Call("createContext", defaultValue[0])
} else {
res = React.Call("createContext")
}

return res.Get("Provider"), res.Get("Consumer")
}

// CloneElement is used to clone and return a new React Element.
// See: https://reactjs.org/docs/react-api.html#cloneelement
func CloneElement(element interface{}, props interface{}, children ...interface{}) *js.Object {

args := []interface{}{
element,
SToMap(props),
}
if len(children) > 0 {
args = append(args, children...)
}

return React.Call("cloneElement", args...)
}
3 changes: 2 additions & 1 deletion react_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ func ForceUpdate(this *js.Object, callback ...func()) {
type ClassDef map[string]interface{}

// NewClassDef will create an empty class definition which can immediately be used
// to create a React component.
// to create a React component. displayName is the text that is shown in Chrome's
// React Developer Tools.
func NewClassDef(displayName string) ClassDef {
def := ClassDef{
render: js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
Expand Down
13 changes: 13 additions & 0 deletions react_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const (

// Unmounting
componentWillUnmount = "componentWillUnmount"

// Error-handling
componentDidCatch = "componentDidCatch"
)

// SetGetDefaultProps sets the getDefaultProps method.
Expand Down Expand Up @@ -131,3 +134,13 @@ func (def ClassDef) SetRender(f func(this *js.Object, props, state Map) interfac
return f(this, props, state)
})
}

// SetComponentDidCatch sets the componentDidCatch method.
func (def ClassDef) SetComponentDidCatch(f func(this *js.Object, err, info *js.Object, props, state Map, setState SetState)) {
def.SetMethod(componentDidCatch, func(this *js.Object, props, state Map, setState SetState, arguments []*js.Object) interface{} {
err := arguments[0]
info := arguments[1]
f(this, err, info, props, state, setState)
return nil
})
}
38 changes: 15 additions & 23 deletions structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,21 @@ func SToMap(s interface{}) map[string]interface{} {
}
}

// jsObjectIsNil return true if x is a js object and is null.
func jsObjectIsNil(x interface{}) bool {
if v, ok := x.(*js.Object); ok && v == nil {
return true
}
return false
}

// jsObjectIsFunction returns true if x is a
// js object and is a js function.
func jsObjectIsFunction(x interface{}) (ret bool) {

v, ok := x.(*js.Object)
if !ok {
// Not a js object
// jsObjectIsNotNil returns true if x is a js object
// and is not null.
func jsObjectIsNotNil(x interface{}) bool {
if v, ok := x.(*js.Object); !ok || v == nil {
return false
}

// Check if it's a function
if _, ok = v.Interface().(func(...interface{}) *js.Object); ok {
return true
}

// jsObjectIsNil return true if x is a js object and is null.
func jsObjectIsNil(x interface{}) bool {
if v, ok := x.(*js.Object); ok && v == nil {
return true
}

return false
}

Expand Down Expand Up @@ -90,7 +82,7 @@ func convertStruct(sIn interface{}) map[string]interface{} {
fieldTag := f.Tag.Get("react")
fieldVal := s.Field(i).Interface()

if fieldTag == "-" || (!jsObjectIsFunction(fieldVal) && strings.HasSuffix(fieldTag, ",omitempty") && (fieldVal == nil || jsObjectIsNil(fieldVal) || reflect.DeepEqual(fieldVal, reflect.Zero(reflect.TypeOf(fieldVal)).Interface()))) {
if fieldTag == "-" || (!jsObjectIsNotNil(fieldVal) && strings.HasSuffix(fieldTag, ",omitempty") && (fieldVal == nil || jsObjectIsNil(fieldVal) || reflect.DeepEqual(fieldVal, reflect.Zero(reflect.TypeOf(fieldVal)).Interface()))) {
// Omit field
continue
}
Expand All @@ -113,25 +105,25 @@ func convertStruct(sIn interface{}) map[string]interface{} {
// Deal with dangerouslySetInnerHTML as a special case
if fieldName == "DangerouslySetInnerHTML" && strings.TrimSuffix(fieldTag, ",omitempty") == "dangerouslySetInnerHTML" {
if fn, ok := fieldVal.(func() interface{}); ok {
mp := SetInnerHTMLFunc(fn)
mp := DangerouslySetInnerHTMLFunc(fn)
out["dangerouslySetInnerHTML"] = mp["dangerouslySetInnerHTML"]
} else {
mp := SetInnerHTML(fieldVal)
mp := DangerouslySetInnerHTML(fieldVal)
out["dangerouslySetInnerHTML"] = mp["dangerouslySetInnerHTML"]
}
continue
}

if fieldTag == "" {
if jsObjectIsFunction(fieldVal) {
if jsObjectIsNotNil(fieldVal) {
out[fieldName] = fieldVal
} else if isStruct(fieldVal) {
out[fieldName] = convertStruct(fieldVal)
} else {
out[fieldName] = fieldVal
}
} else {
if jsObjectIsFunction(fieldVal) {
if jsObjectIsNotNil(fieldVal) {
out[strings.TrimSuffix(fieldTag, ",omitempty")] = fieldVal
} else if isStruct(fieldVal) {
out[strings.TrimSuffix(fieldTag, ",omitempty")] = convertStruct(fieldVal)
Expand Down
14 changes: 7 additions & 7 deletions utility.go → utility_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,22 @@ func (s Set) Convert(base string) map[string]string {
return out
}

// SetInnerHTMLFunc is a convience function used for setting the DOM object's
// inner html. The functon takes a function for the argument.
// DangerouslySetInnerHTMLFunc is a convience function used for setting the DOM
// object's inner html. The functon takes a function for the argument.
// See: https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
func SetInnerHTMLFunc(inside func() interface{}) map[string]interface{} {
func DangerouslySetInnerHTMLFunc(inside func() interface{}) map[string]interface{} {
return map[string]interface{}{
"dangerouslySetInnerHTML": map[string]interface{}{
"__html": inside(),
},
}
}

// SetInnerHTML is a convience function used for setting the DOM object's
// inner html. The function takes the inner html content directly.
// DangerouslySetInnerHTML is a convience function used for setting the DOM
// object's inner html. The function takes the inner html content directly.
// See: https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
func SetInnerHTML(inside interface{}) map[string]interface{} {
return SetInnerHTMLFunc(func() interface{} { return inside })
func DangerouslySetInnerHTML(inside interface{}) map[string]interface{} {
return DangerouslySetInnerHTMLFunc(func() interface{} { return inside })
}

// AddClass adds a new class to an existing list of classes.
Expand Down

0 comments on commit 0bb1f39

Please sign in to comment.