模型属性更改时调用组件方法

时间:2019-01-12 19:01:06

标签: reactjs f# fable-f# elmish

在带有Elmish的寓言应用程序中,我有一个使用react-slick的视图和一个按钮,该按钮应能够在单击时更改幻灯片编号:

Fable.Import.Slick.slider
  [ InitialSlide model.SlideNumber
    AfterChange (SlideTo >> dispatch) ]
  children

Button.button
  [ Button.OnClick (fun _ev -> dispatch (SlideTo 5)) ]
  [ str "Go to slide 5" ]

滑块的react组件由react-slick定义。
我必须自己编写寓言包装,所以它并不完整,因为我仅定义了我需要的属性。

module Fable.Import.Slick

open Fable.Core
open Fable.Core.JsInterop
open Fable.Helpers
open Fable.Helpers.React.Props
open Fable.Import.React

type SliderProps =
    | InitialSlide of int
    | AfterChange of (int -> unit)
    interface IHTMLProp

let slickStyles = importAll<obj> "slick-carousel/slick/slick.scss"
let slickThemeStyles = importAll<obj> "slick-carousel/slick/slick-theme.scss"
let Slider = importDefault<ComponentClass<obj>> "react-slick/lib/slider"
let slider (b: IHTMLProp list) c = React.from Slider (keyValueList CaseRules.LowerFirst b) c

因此,尽管react-slick定义了属性InitialSlide来设置应该在最初显示的幻灯片,但是没有属性可以在以后更新幻灯片。有一个{em> 方法slickGoTo,该方法应该可以执行我想要的操作。但是我仍然不符合Elmish标准,但不知道如何或在何处调用该方法。

我可以想象我必须扩展react组件并监听model属性,然后只要该属性发生更改就调用slickGoTo。但我不知道是否可能。

所以我的问题是:我如何拥有一个按钮,该按钮可以使用由组件定义的slickGoTo来更改单击时的幻灯片编号,同时又不会破坏Elmish架构?

3 个答案:

答案 0 :(得分:1)

要能够调用方法slickGoTo,您需要获取对滑块对象的引用。为此,请使用Ref道具,该道具需要一个(Browser.Element -> unit)类型的函数。它被调用一次。将该元素保存在某个地方,也许在您的模型中:

type Model = {
    ...
    slideNumber : int
    sliderRef   : Browser.Element option
    ...
}

let gotoSlide sliderRef n = 
    match sliderRef with None ->
    | None -> () 
    | Some slider -> slider?slickGoTo n

这样,您可以从更新函数中调用gotoSlide

type Msg =
| SetSliderRef of Browser.Element
| CurrentSlide of int
| GotoSlide    of int
...

let update msg model =
    match msg with
    | SetSliderRef e -> { model with sliderRef   = Some e }  , []
    | CurrentSlide n -> { model with slideNumber = n      }  , []
    | GotoSlide    n -> gotoSlide model.sliderRef n ;   model, []
    ...

像这样创建幻灯片:

slider 
    [ Ref          (SetSliderRef >> dispatch) 
      AfterChange  (CurrentSlide >> dispatch)
      InitialSlide 0
    ]
    slides

我没有测试过任何一个,所以带一点盐。

答案 1 :(得分:1)

在模型中存储对滑块对象的引用的另一种方法是使用可变变量:

let mutable sliderRef   : Browser.Element option = None

let gotoSlide n = 
    match sliderRef with None ->
    | None -> () 
    | Some slider -> slider?slickGoTo n

其余类似:

type Msg =
| CurrentSlide of int
| GotoSlide    of int
...

let update msg model =
    match msg with
    | CurrentSlide n -> { model with slideNumber = n      }  , []
    | GotoSlide    n -> gotoSlide n ;                   model, []

像这样创建滑块:

slider 
    [ Ref          (fun slider = sliderRef <- Some slider) 
      AfterChange  (CurrentSlide >> dispatch)
      InitialSlide 0
    ]
    slides

答案 2 :(得分:0)

我刚刚发现在React中,您以前使用componentWillReceiveProps解决了这些问题-听起来很像我想要的IMO。但这已经过时了,根据我的用法有两个建议:

  

如果您在prop更改时使用componentWillReceiveProps来“重置”某些状态,请考虑使用键使组件完全受控或完全不受控制。

我认为我无法实现Fully controlled版本(必须由组件的作者完成,对吗?),但是fully uncontrolled with a key似乎可以工作。所以我的滑块视图现在看起来像这样:

Fable.Import.Slick.slider
    [ InitialSlide model.SlideNumber
      AfterChange (SlideTo >> dispatch)
      Key (string model.SlideNumber) ]

每次Key更改时,根据文档,都会重新创建组件。现在,这听起来像是很多开销,并且有其自身的缺点(对幻灯片进行动画处理不起作用),但是目前这是最简单的解决方案。有什么问题吗?