@@ -605,4 +605,152 @@ describe('Form List 组件测试', () => {
605605 await mockTimeout ( ) ;
606606 expect ( queryByText ( '用户名必填' ) ) . not . toBeTruthy ( ) ;
607607 } ) ;
608+
609+ test ( 'FormList with shouldUpdate' , async ( ) => {
610+ const TestView = ( ) => {
611+ const [ form ] = Form . useForm ( ) ;
612+
613+ const INIT_DATA = {
614+ services : [
615+ {
616+ modelName : 'modelA' ,
617+ routes : [
618+ { type : 'weight' , weight : 50 , abtest : 'cid' } ,
619+ { type : 'abtest' , weight : 30 , abtest : 'uid' } ,
620+ ] ,
621+ } ,
622+ ] ,
623+ } ;
624+
625+ return (
626+ < Form form = { form } initialData = { INIT_DATA } >
627+ < FormList name = "services" >
628+ { ( fields ) => (
629+ < >
630+ { fields . map ( ( { key, name : serviceName } ) => (
631+ < div key = { key } >
632+ < FormList name = { [ serviceName , 'routes' ] } >
633+ { ( routeFields , { add : addRoute } ) => (
634+ < div >
635+ { routeFields . map ( ( f ) => (
636+ < div key = { f . key } data-route-index = { f . name } >
637+ < FormItem name = { [ f . name , 'type' ] } label = "类型" >
638+ < Input placeholder = { `route-type-${ serviceName } -${ f . name } ` } />
639+ </ FormItem >
640+ < FormItem
641+ shouldUpdate = { ( p , n ) =>
642+ p . services ?. [ serviceName ] ?. routes ?. [ f . name ] ?. type !==
643+ n . services ?. [ serviceName ] ?. routes ?. [ f . name ] ?. type
644+ }
645+ >
646+ { ( { getFieldValue } ) => {
647+ const type = getFieldValue ( [ 'services' , serviceName , 'routes' , f . name , 'type' ] ) ;
648+ if ( type === 'weight' ) {
649+ return (
650+ < FormItem key = { type } name = { [ f . name , 'weight' ] } label = "权重" >
651+ < Input placeholder = { `route-weight-${ serviceName } -${ f . name } ` } />
652+ </ FormItem >
653+ ) ;
654+ }
655+ if ( type === 'abtest' ) {
656+ return (
657+ < FormItem key = { type } name = { [ f . name , 'abtest' ] } label = "分流Key" >
658+ < Input placeholder = { `route-abtest-${ serviceName } -${ f . name } ` } />
659+ </ FormItem >
660+ ) ;
661+ }
662+ return null ;
663+ } }
664+ </ FormItem >
665+ </ div >
666+ ) ) }
667+ < Button id = { `test-add-route-${ serviceName } ` } onClick = { ( ) => addRoute ( ) } >
668+ 新增路由
669+ </ Button >
670+ </ div >
671+ ) }
672+ </ FormList >
673+ </ div >
674+ ) ) }
675+ </ >
676+ ) }
677+ </ FormList >
678+ </ Form >
679+ ) ;
680+ } ;
681+
682+ const { container, getByPlaceholderText } = render ( < TestView /> ) ;
683+
684+ // 第一个 route
685+ expect ( ( getByPlaceholderText ( 'route-type-0-0' ) as HTMLInputElement ) . value ) . toBe ( 'weight' ) ;
686+ expect ( ( getByPlaceholderText ( 'route-weight-0-0' ) as HTMLInputElement ) . value ) . toBe ( '50' ) ;
687+ // 切换到 abtest
688+ const typeInput0 = getByPlaceholderText ( 'route-type-0-0' ) as HTMLInputElement ;
689+ fireEvent . change ( typeInput0 , { target : { value : 'abtest' } } ) ;
690+ await mockTimeout ( ) ;
691+ expect ( ( getByPlaceholderText ( 'route-abtest-0-0' ) as HTMLInputElement ) . value ) . toBe ( 'cid' ) ;
692+ expect ( container . querySelector ( '[placeholder="route-weight-0-0"]' ) ) . toBeFalsy ( ) ;
693+ // 切换回 weight
694+ fireEvent . change ( typeInput0 , { target : { value : 'weight' } } ) ;
695+ await mockTimeout ( ) ;
696+ expect ( ( getByPlaceholderText ( 'route-weight-0-0' ) as HTMLInputElement ) . value ) . toBe ( '50' ) ;
697+
698+ // 第二个 route
699+ expect ( ( getByPlaceholderText ( 'route-type-0-1' ) as HTMLInputElement ) . value ) . toBe ( 'abtest' ) ;
700+ expect ( ( getByPlaceholderText ( 'route-abtest-0-1' ) as HTMLInputElement ) . value ) . toBe ( 'uid' ) ;
701+ // 切换到 weight
702+ const typeInput1 = getByPlaceholderText ( 'route-type-0-1' ) as HTMLInputElement ;
703+ fireEvent . change ( typeInput1 , { target : { value : 'weight' } } ) ;
704+ await mockTimeout ( ) ;
705+ expect ( ( getByPlaceholderText ( 'route-weight-0-1' ) as HTMLInputElement ) . value ) . toBe ( '30' ) ;
706+ expect ( container . querySelector ( '[placeholder="route-abtest-0-1"]' ) ) . toBeFalsy ( ) ;
707+ // 切换回 abtest
708+ fireEvent . change ( typeInput1 , { target : { value : 'abtest' } } ) ;
709+ await mockTimeout ( ) ;
710+ expect ( ( getByPlaceholderText ( 'route-abtest-0-1' ) as HTMLInputElement ) . value ) . toBe ( 'uid' ) ;
711+
712+ // 添加新的 route(没有初始数据)
713+ const addRouteBtn = container . querySelector ( '#test-add-route-0' ) ;
714+ fireEvent . click ( addRouteBtn ) ;
715+ await mockTimeout ( ) ;
716+ const newTypeInput = getByPlaceholderText ( 'route-type-0-2' ) as HTMLInputElement ;
717+ expect ( newTypeInput ) . toBeTruthy ( ) ;
718+ // 新添加的项,type 初始应该为空
719+ expect ( newTypeInput . value ) . toBe ( '' ) ;
720+
721+ // 设置新 route 的 type 为 weight
722+ fireEvent . change ( newTypeInput , { target : { value : 'weight' } } ) ;
723+ await mockTimeout ( ) ;
724+
725+ const newWeightInput = getByPlaceholderText ( 'route-weight-0-2' ) as HTMLInputElement ;
726+ expect ( newWeightInput ) . toBeTruthy ( ) ;
727+ // 新添加的项,weight 初始应该为空
728+ expect ( newWeightInput . value ) . toBe ( '' ) ;
729+
730+ // 切换到 abtest
731+ fireEvent . change ( newTypeInput , { target : { value : 'abtest' } } ) ;
732+ await mockTimeout ( ) ;
733+ const newAbtestInput = getByPlaceholderText ( 'route-abtest-0-2' ) as HTMLInputElement ;
734+ expect ( newAbtestInput ) . toBeTruthy ( ) ;
735+ expect ( newAbtestInput . value ) . toBe ( '' ) ;
736+ // 设置值
737+ fireEvent . change ( newAbtestInput , { target : { value : 'new-key' } } ) ;
738+ await mockTimeout ( ) ;
739+ expect ( newAbtestInput . value ) . toBe ( 'new-key' ) ;
740+
741+ // 切换回 weight
742+ fireEvent . change ( newTypeInput , { target : { value : 'weight' } } ) ;
743+ expect ( container . querySelector ( '[placeholder="route-abtest-0-2"]' ) ) . toBeFalsy ( ) ;
744+ await mockTimeout ( ) ;
745+ // weight 重置为空
746+ const weightInputAgain = getByPlaceholderText ( 'route-weight-0-2' ) as HTMLInputElement ;
747+ expect ( weightInputAgain . value ) . toBe ( '' ) ;
748+
749+ // 切换回 abtest
750+ fireEvent . change ( newTypeInput , { target : { value : 'abtest' } } ) ;
751+ await mockTimeout ( ) ;
752+ // abtest 重置为空
753+ const abtestInputAgain = getByPlaceholderText ( 'route-abtest-0-2' ) as HTMLInputElement ;
754+ expect ( abtestInputAgain . value ) . toBe ( '' ) ;
755+ } ) ;
608756} ) ;
0 commit comments