Вызов метода компонента при изменении свойства модели - PullRequest
0 голосов
/ 12 января 2019

В моем приложении Fable с Elmish у меня есть представление, которое использует реагировать-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-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 для установки слайда, который должен первоначально отображаться, нет свойства для обновления слайда впоследствии. - это метод slickGoTo, который должен делать то, что я хочу. Но я не знаю, как и где вызывать этот метод, оставаясь при этом совместимым с Elmish.

Я мог бы представить, что мне нужно расширить компонент реагирования и прослушать свойство модели, а затем вызывать slickGoTo всякий раз, когда это свойство изменяется. Но я не знаю, возможно ли это.

Итак, мой вопрос: как я могу иметь кнопку, которая изменяет номер слайда при нажатии, используя slickGoTo, который определяется компонентом, не взламывая архитектуру Elmish?

Ответы [ 3 ]

0 голосов
/ 13 января 2019

Я только что обнаружил, что в React вы ранее решали такие вещи, используя componentWillReceiveProps - это очень похоже на то, что я хочу, IMO. Но сейчас это устарело, и есть две рекомендации, основанные на моем использовании:

Если вы использовали componentWillReceiveProps для «сброса» какого-либо состояния при смене реквизита, попробуйте либо сделать компонент полностью контролируемым, либо полностью неконтролируемым с помощью клавиши.

Я не думаю, что смогу реализовать версию Fully controlled (это должен сделать автор компонента, верно?), Но fully uncontrolled with a key, похоже, работает. Итак, мой слайдер теперь выглядит так:

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

Согласно документам, каждый раз, когда меняется Key, компонент воссоздается. Теперь это звучит как много накладных расходов и имеет свои недостатки (анимация слайда не работает), но в настоящее время это самое простое решение. Есть проблемы?

0 голосов
/ 13 января 2019

Альтернативой хранению ссылки на объект-слайдер в модели является использование изменяемой переменной:

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
0 голосов
/ 13 января 2019

Чтобы иметь возможность вызвать метод 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

Я не проверял ничего из этого, так что возьмите зерно соли.

...