首页 > 解决方案 > React.cloneElement 和打字稿

问题描述

我正在尝试使用 React 和 Typescript 制作仪表板。我使用 Card 组件通过 React.cloneElement 将子组件推入其中,并添加 Card 的 setStates 以从子级向 Card 添加类和标题。现在添加的 setState 函数出现错误。在示例中,我删除了不必要的代码和类型以使代码更具可读性。例子:

import React, {useState} from 'react'

function App() {
 return (
  <div>
   <Block
    id="users-component"
    title="Users Table"
   >
    <MyTable class="users" />
   </Block>

   <Block
    id="status-component"
    title="Status Component"
   >
    <Status class="status" />      {/* Property 'renewTitle' is missing in type '{ class: string; }' but required in type 'IStatus'. */}
   </Block>

   <Block
    id="bdays-component"
    title="Bdays Component"
   >
    <Bdays class="bdays" />      {/* Property 'addClasses' is missing in type '{ class: string; }' but required in type 'IBDays'. */}
   </Block>
  </div>
  
 )
}

interface IBlock {
    children: JSX.Element,
    id: string,
    title: string
}

function Block(props: IBlock) {
 const [classes, addClasses] = useState<string[]>([""])
 const [title, renewTitle] = useState<string>(props.title)
 return (
  <div id={props.id} className={classes.join(" ")}>
   <h2>{props.title}</h2>
   {React.cloneElement(props.children, {addClasses, renewTitle})}
  </div>
 )
}

function MyTable(props) {
 return (
  <table> </table>
)
}

interface IStatus {
 class: string,
 renewTitle: (title: string) => void     // ?
}

function Status(props: IStatus) {
 const handleClick = (title) => {
  props.renewTitle(title)     // changes title in Block
 }
 return (
  <div>
   <button onClick={() => handleClick("newTitle")}>New Title</button>
  </div>
)
}

interface IBDays {
 class: string,
 addClasses: (classes: string[]) => void     // ?
}



function Bdays(props: IBDays) {
 const handleClick = (newClass: string) : void => {
  props.addClasses([newClass])     // add new class to array "classes" in it's block
 }
 return (
  <div className={props.class}>
   <button onClick={() => handleClick("newClass")}>New Class</button>
  </div>
)
}

我该如何解决这些问题?

标签: reactjstypescript

解决方案


更新

你需要做一个小的重构。TypeScript 无法确定您正在克隆子组件。我改变了一点逻辑。因为它允许children属性成为一个函数,所以我只是将所需的道具传递给这个函数。

现在 TS 能够弄清楚props里面的论点{(props) => <Status class="status" {...props} />}实际上是有效的

请看评论

import React, { useState } from 'react'

function App() {
  return (
    <div>
      <Block
        id="users-component"
        title="Users Table"
      >{() => <MyTable class="users" />}
      </Block>

      <Block
        id="status-component"
        title="Status Component"
      >{(props) => <Status class="status" {...props} />}
      </Block>

      <Block
        id="bdays-component"
        title="Bdays Component"
      >
        {(props) => <Bdays class="bdays" {...props} />}

      </Block>
    </div>

  )
}

interface IBlock {
  children: (
    props: {
      addClasses: React.Dispatch<React.SetStateAction<string[]>>,
      renewTitle: React.Dispatch<React.SetStateAction<string>>
    }
  ) => React.ReactNode
  id: string,
  title: string
}

const Block: React.FC<IBlock> = (props: IBlock) => {
  const [classes, addClasses] = useState<string[]>([""])
  const [title, renewTitle] = useState<string>(props.title)
  return (
    <div id={props.id} className={classes.join(" ")}>
      <h2>{props.title}</h2>
      {props.children({ addClasses, renewTitle })}
    </div>
  )
}

function MyTable(props: { class: string }) {
  return (
    <table> </table>
  )
}

interface IStatus {
  class: string,
  renewTitle: (title: string) => void
}

function Status(props: IStatus) {
  const handleClick = (title: string) => {
    props.renewTitle(title)
  }
  return (
    <div>
      <button onClick={() => handleClick("newTitle")}>New Title</button>
    </div>
  )
}

interface IBDays {
  class: string,
  addClasses: (classes: string[]) => void
}



function Bdays(props: IBDays) {
  const handleClick = (newClass: string): void => {
    props.addClasses([newClass])
  }
  return (
    <div className={props.class}>
      <button onClick={() => handleClick("newClass")}>New Class</button>
    </div>
  )
}

操场


推荐阅读