React ,自定义钩子慢渲染

Posted

技术标签:

【中文标题】React ,自定义钩子慢渲染【英文标题】:React , custom hook slow render 【发布时间】:2022-01-10 17:15:29 【问题描述】:

您好,我制作了一个自定义钩子,它与通用表单的组件一起使用,但是,我注意到状态更改时它很慢。

#customHook
export const useFormController = (meta) => 
  const  setVisible, setLoading  = useContext(FeedBackContext);
  const itemsRef = useRef([]);
  const 
    control,
    setValue,
    handleSubmit,
    formState:  errors ,
   = useForm<Partial<any>>(
    mode: "onBlur",
    shouldUnregister: true,
    resolver: yupResolver(meta.validation),
  );

  const onRef = function (input) 
    this.itemsRef.current[this.index] = input;
  ;

  const onSubmit = (data: any) => 
    if(meta.onSubmit)
      meta.onSubmit(data);
    else
      setVisible(true);
      setLoading(true);
      meta.service.submit(data);
    
  ;

  const isJsonEmpty = (val = ) => 
    return Object.keys(val).length == 0;
  ;
  const onSubmitIditing = function () 
    let index = ++this.index;
    if (isJsonEmpty(errors) && this.end) 
      handleSubmit(onSubmit)();
     else if (!this.end) 
      this.itemsRef.current[index]._root.focus();
    
  ;
  const setFields = (json) => 
    const items = Object.keys(json);
    const values = Object.values(json)
    console.log('Cambiando fields en formControllser...', json)
    for (let i = 0; i < items.length; i++) 
      //console.log('Cambiando valores...', items[i], values[i])

      setValue(items[i], values[i],  shouldValidate: true )
    
  


  const getItems = () => 
    console.log('Meta namess', meta.names, meta);

    if (!meta && !meta.names) return [];

    return meta.names.map(function (item, index) 
      const isEnd =
        meta.options && meta.options[item] && meta.options[item].end
          ? true
          : false;

      const isSecure =
        meta.options && meta.options[item] && meta.options[item].secure
          ? true
          : false;

      const label = meta.alias ? meta.alias[item] : item;
      const visible = meta.invisibles ? (meta.invisibles[item] ? false : true) : true;
      const def = meta.defaults ? meta.defaults[item] : "";

      const disabled = (val) => 
        const b = meta.disableds ? (meta.disableds[item] ? true : false) : false;
        return b;
      

      return 
        name: item,
        label: label,
        disabled: disabled,
        onRef: onRef.bind( itemsRef: itemsRef, index: index ),
        onSubmitEditing: onSubmitIditing.bind(
          itemsRef: itemsRef,
          index: index,
          end: isEnd,
          errors: errors,
        ),
        visible: visible,
        setFields,
        defaultValue: def,
        errors: errors,
        secureTextEntry: isSecure,
        styles: styles,
        control: control,
        options: meta.options[item] ? meta.options[item] : null,
      ;
    );
  

  const getData = useMemo(() => 
    console.log('Get data calback v2', meta);
    return 
      handleSubmit,
      items: getItems(),
      onSubmit,
      errors,
      setFields
    ;
  , [meta])


  return getData;
;




export const Client: React.FC<any> = React.memo(( navigation, route ) => 


  const 
    alias,
    defaults,
    ubigeoSeleccionado,
    setUbigeoSeleccionado,
    editable,
    inputLabel,
    search,
    getDisabled,
    getInvisibles,
    getAlias,
    getDefaults,
    disableds,
    invisibles,
    searchVisible,
    idTypeDocument,
    currentTypeDocument,
    allTypeDocuments,
    onValueChange,
    onChangeText  = useContext(CreateClientContext);
  const [mode, setMode] = useState(() => 
    return route?.params?.mode;
  )
  const [client, setClient] = useState(() => 
    return route?.params?.client;
  )
  const  dispatchClient  = useContext(GlobalContext);
  const clientService = useClientService();
  const ref = useRef(0);
  const options = useMemo(() => 

    return 
      names: ["ane_numdoc", "ane_razsoc", "ane_alias", "ane_email", "ane_tel", "ane_tel2", "ane_dir"],
      validation: clientValidation,
        alias: alias,
        defaults: defaults,
        disableds: disableds,
        service: 
          submit: (data) => 
            const parse =  ...data, ubigeo_id: ubigeoSeleccionado.ubi_id, ane_tipo_cp: 2, ane_tipdoc: currentTypeDocument.id 
            if (mode == "update") 
              //console.log('Actualizando...', client.id, parse);
              clientService.updateById(client.id, parse)
                .then(ok => 
                  Alert.alert('Actualizaciòn de cliente', "Cliente Actualizado")
                  dispatchClient(
                    type: 'create',
                    payload: ok
                  );

                  setTimeout(() => 
                    navigation.navigate('App', 
                      screen: "Clients"
                    )
                  , 500)

                ).catch(e => 
                  Alert.alert('Actualizaciòn de cliente', "No se pudo actualizar")
                )

             else 
              clientService.create(parse)
                .then(ok => 
                  dispatchClient(
                    type: 'create',
                    payload: ok
                  );
                  Alert.alert('Cliente', "Cliente creado")

                  setTimeout(() => 
                    navigation.navigate('App', 
                      screen: "Clients"
                    )
                  , 500)
                )
                .catch(e => 
                  (e);
                  Alert.alert('Error', "No se pudo crear el cliente")
                )
            
          
        ,
        invisibles: invisibles,
        options: 
          ane_dir: 
            end: true
          ,
        
    

  , [getDisabled,
    getInvisibles,
    getAlias,
    getDefaults])
  const  items, handleSubmit, onSubmit, errors, setFields  = useFormController(options);

  useEffect(() => 
    ref.current++;
  )

  useEffect(() => 
    if (route.params) 
      console.log('Ref current', ref.current);
      setMode(route.params.mode);
      setClient(route.params.client);
    
  , [route.params])
  useEffect(() => 
    // console.log('Mode', mode, client.id);
    if (mode == "update" && client) 
      console.log('cambiando fields'), ref;
      setFields(client)
    
  , [mode, client])

  // useEffect(()=>

  // ,[instanceDocument])

  useEffect(() => 
    console.log('Cambiando cliente...', mode, client);
    console.log(ref.current);
  , [client])

  useEffect(() => 
    //Creación
    console.log('set defaults..', ref.current);
    if (Object.keys(defaults).length > 0) 
      setFields(defaults)
    
  , [getDefaults])
  console.log('Current', ref.current);
  return (
    <StyleProvider style=getTheme(material)>
      <Container style= backgroundColor: "#FAF9FE" >
        <Content style=GlobalStyles.mainContainer>
          <Text style=GlobalStyles.subTitle>Cliente</Text>
          <PickerSearch
          search=search
          editable=editable
          style=styles
            searchVisible=searchVisible
          placeholder=inputLabel
          pickerItems=allTypeDocuments
          onValueChange=onValueChange
            selectedValue=idTypeDocument
          onChangeText=onChangeText
          ></PickerSearch>

          <FormListController
              // top=<Top />
              items=items
              style=GlobalStyles
            ></FormListController>

          <Bottom
            ubigeoSeleccionado=ubigeoSeleccionado
        setUbigeoSeleccionado=setUbigeoSeleccionado
        onSubmit=handleSubmit(onSubmit)
        />



        </Content>
        <AppFooter2 navigation=navigation />
      </Container>
    </StyleProvider>
  );
);

export const FormListController: React.FC<any> = React.memo(( children, items = [], style, top = null, bottom = null ) => 

  console.log('%c Form list controlllser...', "background-color:#ccc");
  console.log('items', items)
  return (
    <>
      <Form style=!!style.form ? style.form : style.formContainer>
        top
        items.map((item: any, index) => 

          return <FormItemController ...item key=index />;

        )
        
        bottom

      </Form>
    </>
  );
);


export const FormItemController: React.FC<any> = React.memo((props: any) => 
  console.log('Form item controller print', props)
  if (props.visible) 
    return (
      <>
        <Controller
          control=props.control
          render=
            ( field:  onChange, onBlur, value  ) => 

              return (
                <Item regular style=props.styles.item>
                  <Label style=props.styles.label>props.label</Label>
                  <Input
                    onBlur=onBlur
                    disabled=props.disabled(value)
                    onChangeText=(value) => onChange(value)
                    secureTextEntry=props.secureTextEntry
                    onSubmitEditing=props.onSubmitEditing
                    value=value
                    ref=props.onRef
                  />
                </Item>
              )
            

          

          defaultValue=props.defaultValue
          name=props.name
        />
        props.errors && props.errors[props.name] && (
          <TextError value=props.errors[props.name].message />
        )
        /* props.options && props.options.errorEmpty && props.errors[""] && (
          <TextError value=props.errors[""].message />
        ) */
      </>
    );
  
  else 
    return <></>
  
);

我使用相同的组件来创建和编辑客户端,但是在编辑和查看 FormItemController 时,日志时间跨度小于 1 秒,但是直到 8 或 10 秒后才会呈现。

这是我的日志输出。

Update cliente...  500ms 

set defaults.. 77
Client num render 77
Client num render 78
Client num render 79
Client num render 80
Client num render 81
Client num render 82
Client num render 83
Client num render 84
Client num render 85
Client num render 86
Client num render 87
Client num render 88
Client num render 89
Client num render 90
Client num render 91
Client num render 92
Client num render 93
Client num render 94
Client num render 95
Client num render 96
Client num render 97
Client num render 98
Client num render 99
Client num render 100  (6-8 seg)

我遇到的问题是我编辑的时候,我用表格创建的时候没有问题,我没有找到瓶颈来改进和防止它变慢。

【问题讨论】:

【参考方案1】:

在尝试了几个选项后,我意识到在传递 setValue 时,我发送了几个空值和不符合表单的对象,过滤了这些数据,使最终渲染通过时间从 8 秒缩短到不到 1 秒

  const setFields = (json) => 
    const items = Object.keys(json);
    const values = Object.values(json)
    for (let i = 0; i < items.length; i++) 
      if (!!values[i] && typeof values[i] != 'object') 
        setValue(items[i], values[i])
      

    
  

【讨论】:

只是一个小的可能的代码改进:迭代 Object.entries(json) 而不是分别抓取键和值。 谢谢,我还在优化这段代码

以上是关于React ,自定义钩子慢渲染的主要内容,如果未能解决你的问题,请参考以下文章

如何使用自定义钩子和 useContext 仅渲染相关组件

为啥 jest 不能为我正在测试的自定义 React 钩子提供 useTranslation 钩子?

用 Jest 模拟 React 自定义钩子

如何在 React 中编写 useComponent 自定义钩子?

用于获取数据的自定义 React 钩子无法正常工作

尽管状态发生了变化,但自定义钩子不会触发组件重新渲染