代码之家  ›  专栏  ›  技术社区  ›  ajnatural

使用state和onChange对useMemo依赖循环做出反应

  •  0
  • ajnatural  · 技术社区  · 4 年前

    在下面的代码中 FormExample 正在渲染。 Form 是一种包装 SampleTextField s并处理所有子字段的更改和状态。我想用 useMemo 这样,一个字段中的更改不会重新渲染另一个字段。

    问题是 handleChange 函数使用其 state 因此,如果我更改第一个字段,然后编辑第二个字段,状态将被第一个字段的旧值覆盖。

    我可以加上包裹把手 useCallback 但它只是在它引用的onChange中产生了同样的问题。

    我还尝试将handleChange添加到useMemo的依赖项数组中,但由于每次状态更改时都会创建一个新的handleChange实例(因为重新呈现),这与没有useMemo是一样的。

    有什么模式可以解决这个问题吗?

    import {TextField} from '@material-ui/core';
    import * as React from 'react';
    import {useContext, useState} from 'react';
    
    const FormContext = React.createContext<any>({});
    const Form = ({state, onChange, children}: any) => {
    
      const handleChange = (name: any, val: any) => {
        onChange({...state, [name]: val});
      }
    
      return (
        <FormContext.Provider value={{state, handleChange}}>
          {children}
        </FormContext.Provider>
      );
    }
    
    const SampleTextField = ({name}: any) => {
      const {state, handleChange} = useContext(FormContext);
      const value = state[name]
    
      return React.useMemo(() => {
        return (
          <TextField name={name} value={value} onChange={(e: any) => handleChange(e.target.name, e.target.value)}/>
        );
      }, [value]);
    }
    
    const FormExample = () => {
      const [state, setState] = useState({text1: "", text2: ""});
    
      return (
        <Form state={state} onChange={setState}>
          <SampleTextField name="text1"/>
          <br/><br/>
          <SampleTextField name="text2"/>
        </Form>
      )
    }
    
    export default FormExample;
    
    1 回复  |  直到 4 年前
        1
  •  0
  •   thedude    4 年前

    你可以用 useCallback 让你的 handleChange 参考稳定。 注意我是如何使用 setState 它接受当前状态作为第一个参数

    import { TextField } from "@material-ui/core";
    import * as React from "react";
    import { useContext, useState, useMemo, useCallback, createContext } from "react";
    
    const FormContext = createContext<any>({});
    const Form = ({ state, onChange, children }: any) => {
      const handleChange = useCallback(
        (name: any, val: any) => {
          onChange((state) => ({ ...state, [name]: val }));
        },
        [onChange]
      );
    
      const context = useMemo(
        () => ({
          state,
          handleChange,
        }),
        [state, handleChange]
      );
    
      return (
        <FormContext.Provider value={context}>{children}</FormContext.Provider>
      );
    };
    
    const SampleTextField = ({ name }: any) => {
      const { state, handleChange } = useContext(FormContext);
      const value = state[name];
    
      return useMemo(
        () => (
          <TextField
            name={name}
            value={value}
            onChange={(e: any) => handleChange(e.target.name, e.target.value)}
          />
        ),
        [value, handleChange]
      );
    };
    
    const FormExample = () => {
      const [state, setState] = useState({ text1: "", text2: "" });
    
      return (
        <Form state={state} onChange={setState}>
          <SampleTextField name="text1" />
          <br />
          <br />
          <SampleTextField name="text2" />
        </Form>
      );
    };
    
    export default FormExample;