输入框失去焦点,陷入重新渲染循环
Input box losing focus, caught in re-render loop
我已经阅读了以前关于类似问题的帖子,并且我已经根据建议(键等,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>
</>
)
}
这里的问题是您在每次渲染时创建 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
并通过所有values/callbacks 作为道具。
示例:
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>
</>
);
}
我已经阅读了以前关于类似问题的帖子,并且我已经根据建议(键等,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>
</>
)
}
这里的问题是您在每次渲染时创建 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
并通过所有values/callbacks 作为道具。
示例:
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>
</>
);
}