![](/img/trans.png)
[英]Why useContext doesn't re-render the component - React.js + Laravel
[英]React.js - Functional component with useState hook doesn't re-render as expected
這是一個開放式問題,因為我確定我要解決的方法不正確。 但是我很好奇為什么React無法像我期望的那樣重新渲染。 我懷疑這與useState
掛鈎與功能組件配對的行為有關。
該代碼在此CodeSandbox鏈接中,並在下面注明了代碼:
function App() {
var foosList = ["foo1", "foo2", "foo3"];
const [selectedFoo, setSelectedFoo] = useState(-1);
const isSelected = i => i === selectedFoo;
return (
<div className="App">
<FoosWrapper>
<TitleSpan>Foos</TitleSpan>
<ListGroup>
{foosList.map((fooItem, i) => (
<ListGroupItem
key={fooItem}
active={isSelected(i)}
onClick={() => setSelectedFoo(i)}
>
{fooItem}
</ListGroupItem>
))}
</ListGroup>
</FoosWrapper>
<BarsWrapper>
<TitleSpan>Bars</TitleSpan>
<Bars foo={foosList[selectedFoo]} />
</BarsWrapper>
</div>
);
}
const Bars = props => {
const [pendingBar, setPendingBar] = useState("");
const [newBars, setNewBars] = useState([]);
const keyPress = e => {
if (e.key === "Enter") {
save(pendingBar);
}
};
const save = bar => {
newBars.push(bar);
setNewBars([...newBars]);
};
return (
<div>
<ListGroup>
<ListGroupItem key={props.foo}>{props.foo}</ListGroupItem>
</ListGroup>
<ListGroup>
{newBars.map(newBar => (
<ListGroupItem key={newBar}>{newBar}</ListGroupItem>
))}
</ListGroup>
<InputGroup>
<Input
placeholder="Add a bar"
onChange={e => setPendingBar(e.target.value)}
onKeyPress={keyPress}
/>
</InputGroup>
</div>
);
};
大致來說,有兩個邏輯小部件: Foos
和Bars
。 Foos
左側, Bars
,就對了。 我想讓用戶選擇一個“ foo”,並且在右側顯示了與所述“ foo”相關聯的不同條形列表。 用戶可以向每個相應的“ foo”添加新的小節。 可以想到foo
與bar
有父級關系。
該Bars
組件維護的用戶添加的酒吧名單。 我的期望是,當選擇一個新的foo時, Bars
組件將重新渲染內部的newBars
集合。 但是,無論在左側選擇了什么“ foo”,該狀態都將徘徊並顯示。
這看起來很奇怪,但是也許我沒有想到以正確的方式考慮React功能組件和掛鈎。 很想了解為什么會出現這種行為,而其他人則希望聽到提出的更有意義的方法。
非常感謝任何見識!
如果要反映層次結構,請初始化酒吧的狀態,以便它與foos的狀態同步。 現在,您的Bars
是一個獨立於App
獨立組件,其狀態保持不變。 這就是我處理這種特殊關系的方法。
function App() {
const foos = useMemo(() => ["foo1", "foo2", "foo3"], []);
const [bars, setBars] = useState(foos.map(() => []));
const [selectedIndex, setSelectedIndex] = useState(0);
const setBar = useCallback(
bar => {
setBars(bars => Object.assign(
[...bars],
{ [selectedIndex]: bar }
));
},
[setBars, selectedIndex]
);
const isSelected = useCallback(
index => index === selectedIndex,
[selectedIndex]
);
const foosList = useMemo(
() => foos.map((foo, index) => (
<ListGroupItem
key={foo}
active={isSelected(index)}
onClick={() => setSelectedIndex(index)}
>
{foo}
</ListGroupItem>
)),
[foos, isSelected, setSelectedIndex]
);
return (
<div className="App">
<FoosWrapper>
<TitleSpan>Foos</TitleSpan>
<ListGroup>{foosList}</ListGroup>
</FoosWrapper>
<BarsWrapper>
<TitleSpan>Bars</TitleSpan>
<Bars
foo={foos[selectedIndex]}
bars={bars[selectedIndex]}
setBars={setBar}
/>
</BarsWrapper>
</div>
);
}
function Bars({ foo, bars, setBars }) {
const [pendingBar, setPendingBar] = useState("");
const barsList = useMemo(
() => bars.map(bar => (
<ListGroupItem key={bar}>{bar}</ListGroupItem>
)),
[bars]
);
const save = useCallback(
bar => { setBars([...bars, bar]); },
[setBars, bars]
);
const change = useCallback(
event => { setPendingBar(event.target.value); },
[setPendingBar]
);
const keyPress = useCallback(
event => { if (event.key === "Enter") save(pendingBar); },
[pendingBar, save]
);
return (
<div>
<ListGroup>
<ListGroupItem key={foo}>{foo}</ListGroupItem>
</ListGroup>
<ListGroup>{barsList}</ListGroup>
<InputGroup>
<Input
placeholder="Add a bar"
onChange={change}
onKeyPress={keyPress}
/>
</InputGroup>
</div>
);
}
我可能對備忘錄掛鈎有些過分,但這應該使您至少可以了解如何以及如何記憶各種值。
請記住,第二個參數是依賴項數組,它確定是對備忘錄的值進行重新計算還是從緩存中檢索。 掛鈎的依賴關系通過引用進行檢查,就像PureComponent
。
我還選擇將selectedIndex
初始化為0
以避免解決沒有選擇foo
時如何處理Bars
的渲染功能的問題。 我將其留給您練習。
如果要顯示所選foo的小節,則需要相應地構造“小節”數據。 最簡單的方法是將“條”數據保持為對象而不是數組的格式。 您可以編寫如下代碼。
const Bars = props => {
const [pendingBar, setPendingBar] = useState("");
const [newBars, setNewBars] = useState({});
const { foo } = props;
const keyPress = e => {
if (e.key === "Enter") {
save(pendingBar);
}
};
const save = bar => {
if (!foo) {
console.log("Foo is not selected");
return;
}
const bars = newBars[foo] || [];
bars.push(bar);
setNewBars({
...newBars,
[foo]: [...bars]
});
};
return (
<div>
<ListGroup>
<ListGroupItem key={props.foo}>{props.foo}</ListGroupItem>
</ListGroup>
<ListGroup>
{newBars[foo] &&
newBars[foo].map(newBar => (
<ListGroupItem key={newBar}>{newBar}</ListGroupItem>
))}
</ListGroup>
<InputGroup>
<Input
placeholder="Add a bar"
onChange={e => setPendingBar(e.target.value)}
onKeyPress={keyPress}
/>
</InputGroup>
</div>
);
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.