Coffeescript 反应:如何向父组件发送信息?
我创建了一个非常简单的导航视图控制器,它模仿了iOS UINavigationController的概念。它基本上只是维护路线的历史记录,并设置推送到另一条路线或弹出的动画 (我希望你不介意咖啡脚本;) 这工作得很好,正如预期的那样。下面是一个使用NavVC递归推送和弹出页面的简单示例Coffeescript 反应:如何向父组件发送信息?,coffeescript,functional-programming,reactjs,Coffeescript,Functional Programming,Reactjs,我创建了一个非常简单的导航视图控制器,它模仿了iOS UINavigationController的概念。它基本上只是维护路线的历史记录,并设置推送到另一条路线或弹出的动画 (我希望你不介意咖啡脚本;) 这工作得很好,正如预期的那样。下面是一个使用NavVC递归推送和弹出页面的简单示例 Page = createView displayName: 'Page' mixins: [React.addons.PureRenderMixin] propTypes: title: R
Page = createView
displayName: 'Page'
mixins: [React.addons.PureRenderMixin]
propTypes:
title: React.PropTypes.string.isRequired
push: React.PropTypes.func.isRequired
pop: React.PropTypes.func
push: ->
@props.push(Math.random().toString(36).substr(2,100))
render: ->
div
className: 'page'
div
className: 'title'
onClick: @push
@props.title
cond @props.pop,
=> div
className: 'back'
onClick: @props.pop
"< BACK"
App = createView
renderScene: (route, push, pop, popFront) ->
Page
key: route
title: route
pop: pop
push: push
render: ->
NavVC
rootScene: 'Hello NavVC'
renderScene: @renderScene
React.render App(), document.body
假设我们有一些按钮,它们是NavVC的兄弟姐妹/父母,它们为我们进行按下和弹出操作
App = createView
getInitialState: -> {}
renderScene: (route, push, pop, popFront) ->
@setState({push, pop})
Page
key: route
title: route
push: ->
@state.push?(Math.random().toString(36).substr(2,100))
render: ->
div
className: 'app'
NavVC
rootScene: 'Hello NavVC'
renderScene: @renderScene
cond @state.pop,
=> div
className: 'left'
onClick: @state.pop
'<'
div
className: 'right'
onClick: @push
'>'
React.render App(), document.body
您可以在以下内容中查看此示例的工作版本
但这种解决方案在某些非常根本的方面似乎是错误的。它不是声明性的,每次涉及两个渲染,并且打破了单向数据流模式。我非常希望在这两个用例中使用相同的组件——子推和弹出,或者父/兄弟推和弹出,或者可能是子推和父弹出的混合。我似乎无法想出一个函数模式来实现这一点,而不打破一些优秀编程的基本规则
编辑1: 所以我想我已经找到了一个正确的方向:EventEmitters。按钮发出事件,NavVC监听这些事件。我们可以通过App组件连接这些事件,NavVC将在挂载时侦听这些事件,并在卸载时注销这些侦听器。这保持了单向数据流
不过,仍然存在一个问题。NavVC需要能够告诉后退按钮是否可弹出,以便后退按钮知道是否可以显示。。。我还没弄明白。好的。这是一个巨大的痛苦,但我找到了一个令人满意的解决方案——尽管如此,我还是希望得到一些意见 我创建了一个代理组件的想法,它适合于可以由父级或兄弟级控制的空间
App = createView
componentWillMount: ->
@PushProxy = createProxy(@renderPush)
@PopProxy = createProxy(@renderPop)
renderPush: (push) ->
div
className: 'right'
onClick: -> push(Math.random().toString(36).substr(2,100))
'>'
renderPop: (pop) ->
cond pop,
=> div
className: 'left'
onClick: pop
'<'
renderScene: (route, push, pop, popFront) ->
Page
key: route
title: route
render: ->
console.log "render app"
div
className: 'app'
@PushProxy()
@PopProxy()
NavVC
rootScene: 'Hello NavVC'
renderScene: @renderScene
pushProxy: @PushProxy
popProxy: @PopProxy
这个方法仍然需要setTimeout为零,但它经过优化,不需要渲染过多的内容,所以我认为我可以接受它
rememberLast = (f) ->
lastArg = (->)
lastResult = false
(arg) ->
if lastArg isnt arg
lastResult = f(arg)
lastArg = arg
return lastResult
defer = (f) ->
(arg) ->
window.setTimeout ->
f(arg)
, 0
createProxy = (renderComponent) ->
value = undefined
listeners = {}
listen = (f) ->
id = Math.random().toString(36).substr(2,100)
listeners[id] = f
if value isnt undefined then f(value)
{stop: -> delete listeners[id]}
dispatch = (x) ->
value = x
for id, f of listeners
f(x)
Proxy = createView
displayName: 'Proxy'
getInitialState: ->
value: undefined
componentWillMount: ->
@listener = listen (value) =>
@setState({value})
componentWillUnMount: ->
@listener.stop()
render: ->
if @state.value isnt undefined
console.log "proxy render"
renderComponent(@state.value)
else
false
Proxy.dispatch = rememberLast defer dispatch
return Proxy
查看演示它的实际操作,并查看日志语句,以查看它的渲染是否达到最佳效果
我很想听听别人的反应,如果这是正确的想法。也许有一种更具原则性的方法可以做到这一点
window.setTimeout =>
@setState({push, pop})
, 0
App = createView
componentWillMount: ->
@PushProxy = createProxy(@renderPush)
@PopProxy = createProxy(@renderPop)
renderPush: (push) ->
div
className: 'right'
onClick: -> push(Math.random().toString(36).substr(2,100))
'>'
renderPop: (pop) ->
cond pop,
=> div
className: 'left'
onClick: pop
'<'
renderScene: (route, push, pop, popFront) ->
Page
key: route
title: route
render: ->
console.log "render app"
div
className: 'app'
@PushProxy()
@PopProxy()
NavVC
rootScene: 'Hello NavVC'
renderScene: @renderScene
pushProxy: @PushProxy
popProxy: @PopProxy
NavVC = createView
# clip
render: ->
console.log "render navvc"
route = @state.stack[@state.stack.length - 1]
pop = @pop if @state.stack.length > 1
popFront = @popFront if @state.stack.length > 1
@props.pushProxy.dispatch(@push)
@props.popProxy.dispatch(pop)
div
className: 'navvc'
Transition
transitionName: @state.transition
@props.renderScene(route, @push, pop, popFront)
rememberLast = (f) ->
lastArg = (->)
lastResult = false
(arg) ->
if lastArg isnt arg
lastResult = f(arg)
lastArg = arg
return lastResult
defer = (f) ->
(arg) ->
window.setTimeout ->
f(arg)
, 0
createProxy = (renderComponent) ->
value = undefined
listeners = {}
listen = (f) ->
id = Math.random().toString(36).substr(2,100)
listeners[id] = f
if value isnt undefined then f(value)
{stop: -> delete listeners[id]}
dispatch = (x) ->
value = x
for id, f of listeners
f(x)
Proxy = createView
displayName: 'Proxy'
getInitialState: ->
value: undefined
componentWillMount: ->
@listener = listen (value) =>
@setState({value})
componentWillUnMount: ->
@listener.stop()
render: ->
if @state.value isnt undefined
console.log "proxy render"
renderComponent(@state.value)
else
false
Proxy.dispatch = rememberLast defer dispatch
return Proxy