reactjs 如何在React中抽象多个相似的功能组件(提供者)?

sd2nnvve  于 2023-01-12  发布在  React
关注(0)|答案(2)|浏览(139)

我在一个React应用程序中有几个providers / contexts可以执行相同的操作,即调用Nestjs应用程序的CRUD操作:

export const CompaniesProvider = ({children}: {children: any}) => {
  const [companies, setCompanies] = useState([])

  const fetchCompany = async () => {
    // etc.
    try {
      // etc.
      setCompanies(responseData)
    } catch (error) {}
  }

  const updateCompany = async () => {
    // etc.
    try {
      // etc.
    } catch (error) {}
  }

  // same for delete, findOne etc..

  return (
    <CompaniesContext.Provider value={{ 
      companies, 
      saveSCompany, 
    }}>
      {children}
    </CompaniesContext.Provider>
  )
}

export const useCompanies = () => useContext(CompaniesContext)

另一个提供者,例如,技术模型看起来完全一样,它只是改变了api url:

export const TechnologiesProvider = ({children}: {children: any}) => {
  const [technologies, setTechnologies] = useState([])

  const fetchTechnology = async () => {
    // etc.
    try {
      // etc.
      setTechnologies(responseData)
    } catch (error) {}
  }

  const updateTechnology = async () => {
    // etc.
    try {
      // etc.
    } catch (error) {}
  }

  // same for delete, findOne etc..

  return (
     <TechnologiesContext.Provider value={{ 
       technologies, 
       savesTechnology, 
     }}>
        {children}
     </TechnologiesContext.Provider>
  )
}

export const useTechnologies = () => useContext(TechnologiesContext)

重构的最好方法是什么?我希望有一个抽象类,实现所有的方法,不同的模型提供者继承方法,抽象类只需要构造函数中的api url。
但是React更喜欢函数组件,这样我们就可以使用像useState这样的钩子。
我是否应该将函数组件改为类组件以便能够重构?但是这样我就失去了钩子的功能,这不是现在的React方式。
另一个想法是将抽象类注入到函数组件中,而提供者只调用方法。
有什么想法吗?

6ju8rftf

6ju8rftf1#

一种实现方法是创建一个工厂函数,该函数获取url(如果需要,还可以获取其他参数)并返回provider&consumer
以下是此类函数的示例:

export const contextFactory = (url: string) => {
  const Context = React.createContext([]); // you can also get the default value from the fn parameters

  const Provider = ({ children }: { children: any }) => {
    const [data, setData] = useState([]);

    const fetch = async () => {
      // here you can use the url to fetch the data
      try {
        // etc.
        setData(responseData);
      } catch (error) {}
    };

    const update = async () => {
      // etc.
      try {
        // etc.
      } catch (error) {}
    };

    // same for delete, findOne etc..

    return (
      <Context.Provider
        value={{
          data,
          save
        }}
      >
        {children}
      </Context.Provider>
    );
  };

  const hook = () => useContext(Context)

  return [Provider, hook]
};

这就是您创建新的提供者和使用者的方法

const [CompaniesProvider, useCompanies] = contextFactory('http://...')
const [TechnologiesProvider, useTechnologies] = contextFactory('http://...')
rdlzhqv9

rdlzhqv92#

最后我创建了一个表示CRUD操作的类:

export class CrudModel {
  private api = externalUrls.api;
  constructor(private modelUrl: string) {}

  async fetchRecords() {
    const url = `${this.api}/${this.modelUrl}`

    try {
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Content-type': 'application/json'
        },
      })
      return await response.json()
    } catch (error) {}
  }

  // removeRecord
  // updateRecord
  // saveRecord
}

然后,对于每个提供者,我减少了代码,因为我只调用CrudModel的一个示例,它具有方法实现。

type Technology = {
  id: number;
  name: string;
}

type Context = {
  technologies: Technology[];
  saveTechnology: any;
  removeTechnology: any;
  updateTechnology: any;
}

const TechnologiesContext = createContext<Context>({
  technologies: [],
  saveTechnology: null,
  removeTechnology: null,
  updateTechnology: null,
})

export const TechnologiesProvider = ({children}: {children: any}) => {
  const [technologies, setTechnologies] = useState([])
  const router = useRouter()

  const crudModel = useMemo(() => {
    return new CrudModel('technologies')
  }, [])

  const saveTechnology = async (createForm: any): Promise<void> => {
    await crudModel.saveRecord(createForm)
    router.reload()
  }

  // fetchTechnologies
  // removeTechnology
  // updateTechnology

  useEffect(() => {
    async function fetchData() {
      const fetchedTechnologies = await crudModel.fetchRecords()
      setTechnologies(fetchedTechnologies)
    }

    fetchData()
  }, [crudModel])

return (
    <TechnologiesContext.Provider value={{ 
      technologies, 
      saveTechnology, 
      removeTechnology ,
      updateTechnology,
    }}>
      {children}
    </TechnologiesContext.Provider>
  )
}

这样我就可以为每个文件设置类型,并且很容易调试/维护。只有一个工厂函数,就像前面的答案,跟随数据流感觉很麻烦。缺点是提供程序文件中有一些重复的代码。不确定我是否可以进一步重构我的答案

相关问题