React 钩子:useState 设置不正确
React hooks: useState not setting correctly
我在父组件中创建了一个 setter 函数并将其传递给它的子组件:
相关部分:
export default function Day({ dayInfo, props }) {
var [timeOfDay, setTimeOfDay] = useState('');
function TimeOfDaySetter(index) {
console.log('index ', index);
if (index === 0) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'AM'));
return <Header as="h1">{timeOfDay}</Header>;
} else if (index === 12) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'PM'));
return <Header as="h1">{timeOfDay}</Header>;
}
}
该函数嵌套在子函数的映射函数中:
{Array.from(Array(amountOfRows)).map((row, index) => {
return (
<React.Fragment key={index}>
<Table.Row>
<Table.Cell rowSpan="2" style={tableStyle}>
{TimeOfDaySetter(index)}
</Table.Cell>
但是这是在跳过第一个条件吗?
任何人都可以帮助解释为什么会这样吗?
完整的父项和组件:
import { Header, Table, TextArea } from 'semantic-ui-react';
import React, { useState, useEffect } from 'react';
export default function Day({ dayInfo, props }) {
var [dayInfoInChild, setDayInfoInChild] = useState([]);
var [timeOfDay, setTimeOfDay] = useState('');
function setExactHourHelper(index) {
return index === 0 ? 12 : '' || index > 12 ? index - 12 : index;
}
function TimeOfDaySetter(index) {
console.log('index ', index);
if (index === 0) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'AM'));
return <Header as="h1">{timeOfDay}</Header>;
} else if (index === 12) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'PM'));
return <Header as="h1">{timeOfDay}</Header>;
}
}
useEffect(() => {
if (dayInfo !== null) {
var modifiedDayInfo = dayInfo
.split(' ')
.map((item) => {
if (item.indexOf(',')) return item.replace(/,/g, '');
})
.join('-');
if (localStorage.getItem(modifiedDayInfo)) {
// setDayInfoInChild(function (dayInfoInChild) {
// return [...setDayInfoInChild, modifiedDayInfo];
// });
console.log(modifiedDayInfo);
} else {
localStorage.setItem(modifiedDayInfo, JSON.stringify({}));
}
}
}, [dayInfo, timeOfDay, timeOfDay]);
function TableLayout({ TimeOfDaySetter }) {
var [amountOfRows, setAmountOfRows] = useState(24);
var [textValue, setTextValue] = useState('');
function handleChange(event) {
setDayInfoInChild(event.target.value);
}
const tableStyle = {
borderLeft: 0,
borderRight: 0,
};
const colorOveride = {
color: '#C1BDBD',
};
return (
<>
<h1>{dayInfo}</h1>
<Table celled structured>
<Table.Body>
{Array.from(Array(amountOfRows)).map((row, index) => {
return (
<React.Fragment key={index}>
<Table.Row>
<Table.Cell rowSpan="2" style={tableStyle}>
{TimeOfDaySetter(index)}
</Table.Cell>
<Table.Cell style={tableStyle}>
{
<strong>
{setExactHourHelper(index)}
:00
</strong>
}
<TextArea
rows={2}
name="textarea"
value={textValue}
onChange={handleChange}
placeholder="Tell us more"
/>
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell style={(tableStyle, colorOveride)}>
{
<strong>
{setExactHourHelper(index)}
:30
</strong>
}
<TextArea rows={2} placeholder="Tell us more" />
</Table.Cell>
</Table.Row>
</React.Fragment>
);
})}
</Table.Body>
</Table>
</>
);
}
{
if (dayInfo === null) {
return <p>Loading...</p>;
}
}
return (
<React.Fragment>
<TableLayout
dayInfo={dayInfo}
timeOfDay={timeOfDay}
TimeOfDaySetter={TimeOfDaySetter}
/>
</React.Fragment>
);
}
我看到你将箭头函数作为 setTimeOfDay 参数传递:
setTimeOfDay((timeOfDay) => (timeOfDay = 'AM'));
相反,您应该像往常一样调用 setTimeOfDay setter,即:
setTimeOfDay('AM');
它应该只是 setTimeOfDay("AM") 并且对于第二个条件块 setTimeOfDay("PM")。通过传入 returns 字符串的函数,您使它变得过于复杂。虽然您可以传入一个 returns 字符串的函数,以便您的 setTimeOfDay 将其设置为字符串,但您的函数有一个基本问题,因为您也在手动更改状态。
注意你的函数中有 "timeOfDay = 'AM'"。这完全违背了 setState 和 useState 的目的,因为您是在直接操纵状态。
传统的 setState 确实有可以接受函数的用例,但我不相信 useState 有。
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
反正这个功能现在对你没用。
另外,在你的useEffect中。即使您的使用效果与 [dayInfo、timeOfDay、timeOfDay] 相关联,它也从未实际修改过,至少在您显示的代码中是这样。因此,您的 Day 组件仅在装载期间获取 dayInfo 的更改,而不是在重新渲染时。
另外,除非我遗漏了什么,因为当你调用
时,你的状态是在你的 Day 组件中管理的,而不是每个单独的 React Fragment
{Array.from(Array(amountOfRows)).map((row, index) => {
return (
<React.Fragment key={index}>
<Table.Row>
<Table.Cell rowSpan="2" style={tableStyle}>
{TimeOfDaySetter(index)}
</Table.Cell>
您只是在地图上的每次迭代中更改 Day 组件的状态,这将导致您的 React 片段重新呈现。因此只有最后一个条件会被遵守,因为每个片段都会重新渲染。最好将道具传递给新的组件项而不是在每次迭代时调用状态,使您的状态更复杂以管理每个片段,或者在每个组件内本地管理您的状态。
我在父组件中创建了一个 setter 函数并将其传递给它的子组件:
相关部分:
export default function Day({ dayInfo, props }) {
var [timeOfDay, setTimeOfDay] = useState('');
function TimeOfDaySetter(index) {
console.log('index ', index);
if (index === 0) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'AM'));
return <Header as="h1">{timeOfDay}</Header>;
} else if (index === 12) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'PM'));
return <Header as="h1">{timeOfDay}</Header>;
}
}
该函数嵌套在子函数的映射函数中:
{Array.from(Array(amountOfRows)).map((row, index) => {
return (
<React.Fragment key={index}>
<Table.Row>
<Table.Cell rowSpan="2" style={tableStyle}>
{TimeOfDaySetter(index)}
</Table.Cell>
但是这是在跳过第一个条件吗?
任何人都可以帮助解释为什么会这样吗?
完整的父项和组件:
import { Header, Table, TextArea } from 'semantic-ui-react';
import React, { useState, useEffect } from 'react';
export default function Day({ dayInfo, props }) {
var [dayInfoInChild, setDayInfoInChild] = useState([]);
var [timeOfDay, setTimeOfDay] = useState('');
function setExactHourHelper(index) {
return index === 0 ? 12 : '' || index > 12 ? index - 12 : index;
}
function TimeOfDaySetter(index) {
console.log('index ', index);
if (index === 0) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'AM'));
return <Header as="h1">{timeOfDay}</Header>;
} else if (index === 12) {
setTimeOfDay((timeOfDay) => (timeOfDay = 'PM'));
return <Header as="h1">{timeOfDay}</Header>;
}
}
useEffect(() => {
if (dayInfo !== null) {
var modifiedDayInfo = dayInfo
.split(' ')
.map((item) => {
if (item.indexOf(',')) return item.replace(/,/g, '');
})
.join('-');
if (localStorage.getItem(modifiedDayInfo)) {
// setDayInfoInChild(function (dayInfoInChild) {
// return [...setDayInfoInChild, modifiedDayInfo];
// });
console.log(modifiedDayInfo);
} else {
localStorage.setItem(modifiedDayInfo, JSON.stringify({}));
}
}
}, [dayInfo, timeOfDay, timeOfDay]);
function TableLayout({ TimeOfDaySetter }) {
var [amountOfRows, setAmountOfRows] = useState(24);
var [textValue, setTextValue] = useState('');
function handleChange(event) {
setDayInfoInChild(event.target.value);
}
const tableStyle = {
borderLeft: 0,
borderRight: 0,
};
const colorOveride = {
color: '#C1BDBD',
};
return (
<>
<h1>{dayInfo}</h1>
<Table celled structured>
<Table.Body>
{Array.from(Array(amountOfRows)).map((row, index) => {
return (
<React.Fragment key={index}>
<Table.Row>
<Table.Cell rowSpan="2" style={tableStyle}>
{TimeOfDaySetter(index)}
</Table.Cell>
<Table.Cell style={tableStyle}>
{
<strong>
{setExactHourHelper(index)}
:00
</strong>
}
<TextArea
rows={2}
name="textarea"
value={textValue}
onChange={handleChange}
placeholder="Tell us more"
/>
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell style={(tableStyle, colorOveride)}>
{
<strong>
{setExactHourHelper(index)}
:30
</strong>
}
<TextArea rows={2} placeholder="Tell us more" />
</Table.Cell>
</Table.Row>
</React.Fragment>
);
})}
</Table.Body>
</Table>
</>
);
}
{
if (dayInfo === null) {
return <p>Loading...</p>;
}
}
return (
<React.Fragment>
<TableLayout
dayInfo={dayInfo}
timeOfDay={timeOfDay}
TimeOfDaySetter={TimeOfDaySetter}
/>
</React.Fragment>
);
}
我看到你将箭头函数作为 setTimeOfDay 参数传递:
setTimeOfDay((timeOfDay) => (timeOfDay = 'AM'));
相反,您应该像往常一样调用 setTimeOfDay setter,即:
setTimeOfDay('AM');
它应该只是 setTimeOfDay("AM") 并且对于第二个条件块 setTimeOfDay("PM")。通过传入 returns 字符串的函数,您使它变得过于复杂。虽然您可以传入一个 returns 字符串的函数,以便您的 setTimeOfDay 将其设置为字符串,但您的函数有一个基本问题,因为您也在手动更改状态。
注意你的函数中有 "timeOfDay = 'AM'"。这完全违背了 setState 和 useState 的目的,因为您是在直接操纵状态。
传统的 setState 确实有可以接受函数的用例,但我不相信 useState 有。
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
反正这个功能现在对你没用。
另外,在你的useEffect中。即使您的使用效果与 [dayInfo、timeOfDay、timeOfDay] 相关联,它也从未实际修改过,至少在您显示的代码中是这样。因此,您的 Day 组件仅在装载期间获取 dayInfo 的更改,而不是在重新渲染时。
另外,除非我遗漏了什么,因为当你调用
时,你的状态是在你的 Day 组件中管理的,而不是每个单独的 React Fragment{Array.from(Array(amountOfRows)).map((row, index) => {
return (
<React.Fragment key={index}>
<Table.Row>
<Table.Cell rowSpan="2" style={tableStyle}>
{TimeOfDaySetter(index)}
</Table.Cell>
您只是在地图上的每次迭代中更改 Day 组件的状态,这将导致您的 React 片段重新呈现。因此只有最后一个条件会被遵守,因为每个片段都会重新渲染。最好将道具传递给新的组件项而不是在每次迭代时调用状态,使您的状态更复杂以管理每个片段,或者在每个组件内本地管理您的状态。