1. 程式人生 > 實用技巧 >taro3.x: 父元件傳子元件,元件之間相互更新問題

taro3.x: 父元件傳子元件,元件之間相互更新問題

父元件呼叫子元件方法:實現父元件滾動下拉更新分頁

子元件註冊方法傳給父元件:

useImperativeHandle(ref, () => ({
        innerFn: handleScrollToLower
    }), [page.totalPage, param.currentPage])

    const handleScrollToLower = useCallback(() => {
        if (page.totalPage > param.currentPage) {
            setLoading(true)
            setParam({
                currentPage: param.currentPage + 1
            })
        } else {
            setShowEmpty(true)
        }
    }, [page.totalPage, param.currentPage])

使用useImperativeHandle往ref註冊handleScrollToLower方法傳入父元件進行接收

const ref = useRef<any>({})

const handleScrollToLower = useCallback(() => {
    ref.current.innerFn && ref.current.innerFn()
  }, [])

const getPhotoRender = useMemo(() => (
    <Photos type="4" title={searchTitle} ref={ref} />
  ), [searchTitle])

 

使用useMemo依賴更新子元件

const getPhotoRender = useMemo(() => (
    <Photos type="4" title={searchTitle} ref={ref} />
  ), [searchTitle])

  

父元件Index:

import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { View, Input, ScrollView, Text } from '@tarojs/components'
import classnames from 'classnames'

import app from '@services/request'
import api from '@services/api'
import NavBar from '@components/navbar'
import useNavData from '@hooks/useNavData'
import Photos from '@components/photos'
import Videos from '@components/videos'
import Articles from '@components/articles'
import './index.scss'

const Index = () => {
  const { appHeaderHeight, contentHeight } = useNavData()
  const [searchTitle, setSearchTitle] = useState
<string>('') const [scroll, setScroll] = useState<any>({}) const [navData, setNavData] = useState<any[]>([]) const [currentNav, setCurrentNav] = useState<any>({}) const ref = useRef<any>({}) useEffect(() => { app.request({ url: app.apiUrl(api.getNewsCate), data: { status: 1 } }, { loading: false }).then((result: any) => { setNavData(result) setCurrentNav(result[0]) }) }, []) const handleScroll = (e: any) => { const top = e.detail.scrollTop if (top > 200) { setScroll({ ...scroll, fixed: true, style: { top: appHeaderHeight } }) } if (top <= 200 && scroll.fixed) { setScroll({ ...scroll, fixed: false, style: {} }) } } const toTop = () => { setScroll({ top: Math.random(), fixed: false, style: {} }) } const handleNavClick = (item: any) => { setCurrentNav(item) } const handleScrollToLower = useCallback(() => { ref.current.innerFn && ref.current.innerFn() }, []) const handleInputChange = (e: any) => { console.log(e.detail) setSearchTitle(e.detail.value) } const getPhotoRender = useMemo(() => ( <Photos type="4" title={searchTitle} ref={ref} /> ), [searchTitle]) return ( <View className="index"> <NavBar /> <ScrollView scrollY style={{ maxHeight: contentHeight }} scrollWithAnimation scrollTop={scroll.top} onScroll={handleScroll} lowerThreshold={30} onScrollToLower={handleScrollToLower} > <View className="header"> <View className="logo"></View> <View className="title">國脈房產案例集</View> </View> <View className="search"> <View className="search-content"> <View className="iconfont icon-search"></View> <Input className="search-input" placeholder="搜尋" onBlur={handleInputChange}></Input> </View> </View> <View className={classnames('indexnav', scroll.fixed && 'fixed')} style={scroll.style}> <ScrollView scrollX> { navData.map((item: any, index: number) => ( <View key={index} onClick={() => handleNavClick(item)} className={classnames('indexnav-item', currentNav.id === item.id && 'actived')}> <View className="name">{item.title}</View> </View> )) } </ScrollView> </View> <View className="content"> {currentNav.type === '4' && getPhotoRender} {currentNav.type === '5' && <Videos />} {currentNav.type === '3' && <Articles />} </View> </ScrollView> <View className="action"> { scroll.fixed && <View className="action-item" onClick={toTop}> <View className="item-icon"> <View>TOP</View> </View> </View> } <View className="action-item"> <View className="item-icon"> <View className="iconfont icon-telephone-out"></View> </View> <View className="item-text"> <Text>聯絡我們</Text> </View> </View> </View> </View> ) } export default Index

子元件:

import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react'
import Taro from '@tarojs/taro'
import { View, Image, Text } from '@tarojs/components'

import app from '@services/request'
import api from '@services/api'
import { getTotalPage, INIT_PAGE, IPage } from '@utils/page'

import './index.scss'

interface IParam {
    currentPage: number
}

const INIT_PARAM: IParam = {
    currentPage: 1
}

interface IProps {
    type: string,
    title?: string
}

const Photos = (props: IProps, ref: any) => {
    const PAGE_LIMIT = 10
    const [param, setParam] = useState<IParam>(INIT_PARAM)
    const [page, setPage] = useState<IPage>(INIT_PAGE)
    const [loading, setLoading] = useState<boolean>(false)
    const [showEmpty, setShowEmpty] = useState<boolean>(false)
    const [photos, setPhotos] = useState<any[]>([])

    useEffect(() => {
        app.request({
            url: app.apiUrl(api.newsList),
            data: {
                page: param.currentPage,
                limit: PAGE_LIMIT,
                type: props.type,
                title: props.title
            }
        }, { loading: false }).then((result: any) => {
            setLoading(false)
            const totalPage = getTotalPage(PAGE_LIMIT, result.pagination.totalCount)
            setShowEmpty(totalPage <= INIT_PARAM.currentPage)
            setPage({
                totalCount: result.pagination.totalCount,
                totalPage
            })

            if (param.currentPage === 1) {
                setPhotos(result.data)
            } else {
                setPhotos([...photos, ...result.data])
            }
        })
    }, [param.currentPage, props.title])

    useImperativeHandle(ref, () => ({
        innerFn: handleScrollToLower
    }), [page.totalPage, param.currentPage])

    const handleScrollToLower = useCallback(() => {
        if (page.totalPage > param.currentPage) {
            setLoading(true)
            setParam({
                currentPage: param.currentPage + 1
            })
        } else {
            setShowEmpty(true)
        }
    }, [page.totalPage, param.currentPage])

    const toPhotoList = useCallback((id: string) => {
        Taro.navigateTo({
            url: `/pages/photo/index?id=${id}`
        })
    }, [])

    return (
        <View className="photos">
            {
                photos.map((item: any, index: number) => (
                    <View key={index} className="item" onClick={() => toPhotoList(item.id)}>
                        <View className="item-photo">
                            <Image src={item.image_path}></Image>
                        </View>
                        <View className="item-text">
                            <View className="title">{item.title}</View>
                            <View className="sub-title">{item.sub_title}</View>
                        </View>
                        <View className="item-mask"></View>
                    </View>
                ))
            }
            {
                loading &&
                <View className="empty-container">
                    <Text>正在載入中...</Text>
                </View>
            }
            {
                showEmpty &&
                <View className="empty-container">
                    <Text>沒有更多資料了</Text>
                </View>
            }
        </View>
    )
}

export default forwardRef(Photos)

圖例: