我正在使用Diode 1.0.0和scalajs-react 0.11.1。
用例:
Pot
用于异步获取的图像Pot
为Empty
,更新其模型片段使用简单的方法,这会导致以下情况(事件的顺序可能不同):
GetImageAction
。模型片段Pot
已更新为Pending
。Empty
Pot
,因此他们会再次触发GetImageAction
。这会导致一个巨大的GetImageAction
调用和重新渲染树。
有些问题:
shouldComponentUpdate
来达到此目的。我现在正在为每个子组件添加一个React键。这摆脱了关于唯一键的React警告,但遗憾的是没有解决上述问题。即使他们的shouldComponentUpdate
方法返回false
,孩子也会重新渲染。
来自ParentComponent.render()
:
items.zipWithIndex.map { case (_, i) =>
proxy.connector.connect(
proxy.modelReader.zoom(_.get(i)), s"child_$i": js.Any).
apply(childComponent(props.router, _))
}
我尝试在父组件中实现侦听器功能,但不幸的是,子组件仍然已卸载并重新装入。这是我的父组件的代码:
package kidstravel.client.components
import diode.data.{Empty, Pot}
import diode.react.ModelProxy
import diode.react.ReactPot._
import diode.{Action, ModelR}
import japgolly.scalajs.react.extra.router.RouterCtl
import japgolly.scalajs.react.vdom.prefix_<^._
import japgolly.scalajs.react.{BackendScope, ReactComponentB, _}
import kidstravel.client.KidsTravelMain.Loc
import kidstravel.client.services.{KidsTravelCircuit, RootModel}
case class TileProps[T](router: RouterCtl[Loc], proxy: ModelProxy[T])
/**
* Render sequence of models as tiles.
*/
trait Tiles {
// The type of the model objects.
type T <: AnyRef
/**
* Override to provide the action to obtain the model objects.
* @return An action.
*/
def getAction: Action
/**
* Returns the tile component class.
* @return
*/
def tileComponent: ReactComponentC.ReqProps[TileProps[T], _, _, _ <: TopNode]
case class Props(router: RouterCtl[Loc], proxy: ModelProxy[Pot[Seq[T]]])
class Backend($: BackendScope[Props, Pot[Seq[T]]]) {
private var unsubscribe = Option.empty[() => Unit]
def willMount(props: Props) = {
val modelReader = props.proxy.modelReader.asInstanceOf[ModelR[RootModel, Pot[Seq[T]]]]
Callback {
unsubscribe = Some(KidsTravelCircuit.subscribe(modelReader)(changeHandler(modelReader)))
} >> $.setState(modelReader())
}
def willUnmount = Callback {
unsubscribe.foreach(f => f())
unsubscribe = None
}
private def changeHandler(modelReader: ModelR[RootModel, Pot[Seq[T]]])(
cursor: ModelR[RootModel, Pot[Seq[T]]]): Unit = {
// modify state if we are mounted and state has actually changed
if ($.isMounted() && modelReader =!= $.accessDirect.state) {
$.accessDirect.setState(modelReader())
}
}
def didMount = $.props >>= (p => p.proxy.value match {
case Empty => p.proxy.dispatch(getAction)
case _ => Callback.empty
})
def render(props: Props) = {
println("Rendering tiles")
val proxy = props.proxy
<.div(
^.`class` := "row",
proxy().renderFailed(ex => "Error loading"),
proxy().renderPending(_ > 100, _ => <.p("Loading …")),
proxy().render(items =>
items.zipWithIndex.map { case (_, i) =>
//proxy.connector.connect(proxy.modelReader.zoom(_.get(i)), s"tile_$i": js.Any).apply(tileComponent(props.router, _))
//proxy.connector.connect(proxy.modelReader.zoom(_.get(i))).apply(tileComponent(props.router, _))
//proxy.wrap(_.get(i))(tileComponent(_))
tileComponent.withKey(s"tile_$i")(TileProps(props.router, proxy.zoom(_.get(i))))
}
)
)
}
}
private val component = ReactComponentB[Props]("Tiles").
initialState(Empty: Pot[Seq[T]]).
renderBackend[Backend].
componentWillMount(scope => scope.backend.willMount(scope.props)).
componentDidMount(_.backend.didMount).
build
def apply(router: RouterCtl[Loc], proxy: ModelProxy[Pot[Seq[T]]]) = component(Props(router, proxy))
}
答案 0 :(得分:1)
这很可能是因为在render方法中调用了connect
。这将强制卸载/重新安装所有子组件。例如,当挂载父组件时,最好调用connect
,然后在渲染中使用结果。
或者,您可以完全跳过connect
并直接在父组件中实现更改侦听器。当项目集合发生更改时,更新强制重新呈现更新所有已更改组件的状态。使用shouldComponentUpdate
允许React确定哪些组件确实发生了变化。