Вызов метода компонента при изменении свойства модели
В моем приложении Fable с Elmish у меня есть вид, использующий реагирующий щелчок и кнопку, которая должна изменять номер слайда при нажатии:
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 ответа
Альтернативой хранению ссылки на объект-слайдер в модели является использование изменяемой переменной:
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
Чтобы иметь возможность вызывать метод 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
Я не проверял ничего из этого, так что возьмите зерно соли.
Я только что обнаружил, что в React вы ранее решали такие вещи, используя componentWillReceiveProps
- Это звучит очень похоже на то, что я хочу, ИМО. Но сейчас это устарело, и есть две рекомендации, основанные на моем использовании:
Если вы использовали componentWillReceiveProps для "сброса" какого-либо состояния при изменении реквизита, попробуйте либо сделать компонент полностью контролируемым, либо полностью неконтролируемым с помощью ключа.
Я не думаю, что смогу реализовать Fully controlled
версия (это должен сделать автор компонента, верно?), но fully uncontrolled with a key
похоже на работу. Итак, мой вид слайдера теперь выглядит так:
Fable.Import.Slick.slider
[ InitialSlide model.SlideNumber
AfterChange (SlideTo >> dispatch)
Key (string model.SlideNumber) ]
Согласно документам каждый раз Key
изменения, компонент воссоздан. Теперь это звучит как много накладных расходов и имеет свои недостатки (анимация слайда не работает), но в настоящее время это самое простое решение. Есть проблемы?