【问题标题】:Input box losing focus, caught in re-render loop输入框失去焦点,陷入重新渲染循环
【发布时间】:2022-01-24 15:20:37
【问题描述】:

我已经阅读了以前关于类似问题的帖子,并根据建议尽可能多地添加(键等,onFocus(仅适用于单个输入)),不幸的是,我的反应知识不足以修复问题。

输入单个字符时输入框失去焦点,正在重新渲染组件(我认为)我可以继续在框中输入(每次点击输入calculator working)和结果和总和所有工作符合预期。

任何帮助将不胜感激 谢谢

import { Table, THead, Tr, Th, TBody, Td } from '@twilio-paste/core/table'
import { Text } from '@twilio-paste/text';
import { Input } from '@twilio-paste/core/input';
import { Label } from '@twilio-paste/core/label';
import { styled } from '@twilio-paste/styling-library';
import { Box } from '@twilio-paste/core/box'

const StyledCheckboxContainer = styled(Box)`
  > * {
    width: 50%;
    margin-bottom: 8px;}`
const StyledColumn = styled.div`
  display: flex;
  flex-direction: column;
  flex-basis: 100%;
  flex: 1;
  margin-left: 8px;
  margin-right: 8px; `
const StyledText = styled.div`
  font-size: 12px;
  color: #606B85;`
const StyledInputContainer = styled.div`
  margin-bottom: 24px;`


export default function BudgetCalculator() {
  const [monthlyAmount, setMonthlyAmount] = useState(0);
  const [interestRate, setInterestRate] = useState(0);
  const [deposit, setDeposit] = useState(0);
  const [feeAtStart, setFeeAtStart] = useState(0);
  const [feeAtEnd, setFeeAtEnd] = useState(0);

  var decimalInterest = 1 + (interestRate / 100)
  var monthlyAPR = ((decimalInterest) ** (1 / 12)) - 1
  function annuityCalculation(monthDynamicArr: number) {
    var total = monthlyAmount * ((1 - (1 + monthlyAPR) ** -(monthDynamicArr))) / monthlyAPR
    return total
  }
  let monthsArr = [12, 24, 30, 36, 42, 48, 54, 60]
  let monthDynamicArr = monthsArr.map(month => {
    return annuityCalculation(month)
  })

  let totalWithFee = monthDynamicArr.map(month => (month + +feeAtEnd) - feeAtStart)
  let totalWithDeposit = totalWithFee.map(month => month + +deposit)

  const InputBoxes = () => {
    return (
      <StyledColumn>
        <StyledInputContainer>
          <Label required htmlFor="monthly_budget">Monthly Budget</Label>
          <Input
            key="monthly_budget"
            type="number"
            value={`${monthlyAmount}`}
            onChange={(e) => setMonthlyAmount(parseInt(e.target.value) || 0)}
            placeholder="0"
            insertBefore={<Text as="span" fontWeight="fontWeightSemibold">£</Text>}
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label required htmlFor="apr">APR</Label>
          <Input
            key="apr"
            type="number"
            value={`${interestRate}`}
            onChange={(e) => setInterestRate(parseInt(e.target.value) || 0)}
            placeholder="0"
            insertAfter={<Text as="span" fontWeight="fontWeightSemibold">%</Text>}
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label htmlFor="deposit">Deposit</Label>
          <Input type="number"
            key="deposit"
            value={`${deposit}`}
            onChange={(e) => setDeposit(parseInt(e.target.value) || 0)}
            placeholder="0"
            insertBefore={<Text as="span" fontWeight="fontWeightSemibold">£</Text>}
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label htmlFor="fee_at_start">Fee at start</Label>
          <Input
            key="fee_at_start"
            type="number"
            value={`${feeAtStart}`}
            onChange={(e) => setFeeAtStart(parseInt(e.target.value) || 0)}
            placeholder="0"
            insertBefore={<Text as="span" fontWeight="fontWeightSemibold">£</Text>}
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label htmlFor="fee_at_end">Fee at end</Label>
          <Input key="fee_at_end"
            type="number"
            value={`${feeAtEnd}`}
            onChange={(e) => setFeeAtEnd(parseInt(e.target.value) || 0)}
            placeholder="0"
            insertBefore={<Text as="span" fontWeight="fontWeightSemibold">£</Text>}
          />
        </StyledInputContainer>
      </StyledColumn>
    );
  }
  const tableMonths = monthsArr.map((month) => {
    return (
      <Tr>
        <Td key={"months"}>
          {month} months
        </Td>
      </Tr>
    )
  })

  const tableLoan = totalWithFee.map((money) => {
    return (
      <Tr>
        <Td key={"fee"}>
          £ {money.toFixed(2)}
        </Td>
      </Tr>
    )
  })
  const tableDeposit = totalWithDeposit.map((money) => {

    return (
      <Tr>
        <Td key={"totalDeposit"}>
          £ {money.toFixed(2)}
        </Td>
      </Tr>
    )
  })

  const TableExample = () => {
    return (
      <StyledColumn>
        <Table>
          <THead>
            <Tr>
              <Th>Term</Th>
              <Th>Loan</Th>
              <Th>Total</Th>
            </Tr>
          </THead>
          <TBody>
            <Td>
              <Tr>
                {tableMonths}
              </Tr>
            </Td>
            <Td>
              <Tr>
                {tableLoan}
              </Tr>
            </Td>
            <Td>
              <Tr>
                {tableDeposit}
              </Tr>
            </Td>
          </TBody>
        </Table>
      </StyledColumn >
    )
  }

  return (
    <>
      <StyledCheckboxContainer
        display="flex"
        flexWrap="wrap"
      >
        <InputBoxes />
        <TableExample />
      </StyledCheckboxContainer>
      <StyledText>
        Approximate
      </StyledText>
    </>
  )
}

【问题讨论】:

    标签: input react-hooks render


    【解决方案1】:

    这里的问题是您在每次渲染时都创建了InputBoxes 组件。由于这是一个新的组件引用,它会卸载任何先前存在的版本并安装新版本。这就是输入焦点丢失的原因。

    不要在另一个 React 组件的函数组件体中声明 React 组件。

    阻力最小的路径是将 InputBoxes 重命名为 inputBoxes 并将其声明为 JSX 文字而不是 React 组件。

    const inputBoxes = (
      <StyledColumn>
        <StyledInputContainer>
          <Label required htmlFor="monthly_budget">
            Monthly Budget
          </Label>
          <Input
            ...
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label required htmlFor="apr">
            APR
          </Label>
          <Input
            ...
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label htmlFor="deposit">Deposit</Label>
          <Input
            ...
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label htmlFor="fee_at_start">Fee at start</Label>
          <Input
            ...
          />
        </StyledInputContainer>
        <StyledInputContainer>
          <Label htmlFor="fee_at_end">Fee at end</Label>
          <Input
            ...
          />
        </StyledInputContainer>
      </StyledColumn>
    );
    

    ...

    return (
      <>
        <StyledCheckboxContainer display="flex" flexWrap="wrap">
          {inputBoxes}
          <TableExample />
        </StyledCheckboxContainer>
        <StyledText>Approximate</StyledText>
      </>
    );
    

    另一种方法是保留InputBoxes 一个组件并将其声明为在外部 BudgetCalculator 并将所有值/回调作为道具传递。

    例子:

    const InputBoxes = ({
      monthlyAmount,
      setMonthlyAmount,
      interestRate,
      setInterestRate,
      deposit,
      setDeposit,
      feeAtStart,
      setFeeAtStart,
      feeAtEnd,
      setFeeAtEnd
    }) => {
      return (
        <StyledColumn>
          <StyledInputContainer>
            <Label required htmlFor="monthly_budget">
              Monthly Budget
            </Label>
            <Input
              ...
            />
          </StyledInputContainer>
          <StyledInputContainer>
            <Label required htmlFor="apr">
              APR
            </Label>
            <Input
              ...
            />
          </StyledInputContainer>
          <StyledInputContainer>
            <Label htmlFor="deposit">Deposit</Label>
            <Input
              ...
            />
          </StyledInputContainer>
          <StyledInputContainer>
            <Label htmlFor="fee_at_start">Fee at start</Label>
            <Input
              ...
            />
          </StyledInputContainer>
          <StyledInputContainer>
            ...
            />
          </StyledInputContainer>
        </StyledColumn>
      );
    };
    
    function BudgetCalculator() {
      ...
    
      return (
        <>
          <StyledCheckboxContainer display="flex" flexWrap="wrap">
            <InputBoxes
              {...{
                monthlyAmount,
                setMonthlyAmount,
                interestRate,
                setInterestRate,
                deposit,
                setDeposit,
                feeAtStart,
                setFeeAtStart,
                feeAtEnd,
                setFeeAtEnd
              }}
            />
            <TableExample />
          </StyledCheckboxContainer>
          <StyledText>Approximate</StyledText>
        </>
      );
    }
    

    【讨论】:

    • 非常感谢!我使用了第二种解决方案,因为 是库中的组件,因此分离代码是最好的选择。非常感谢您的帮助。
    猜你喜欢
    • 2014-04-29
    • 2018-12-12
    • 2023-01-20
    • 2017-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-20
    • 2021-03-19
    相关资源
    最近更新 更多