使用 react-bootstrap,我正在创建一个“待办事项”列表,当我选中一个复选框时,我想在文本中画一条线。我怀疑我的问题在于尝试在 map 函数中使用 ref 。我也不认为我在这里正确使用状态。
我正在学习 react 和 bootstrap 的各个方面,我对如何将 ref 与数组一起使用有点困惑。在网上查看一些解决方案,我还没有找到一种方法在使用 react-bootstrap 2.7.2 和 bootstrap 5.2.3.
时在这样的数组中正确使用 ref我试过同时使用
useRef()
和 useRef([])
来开始。使用下面的代码,当我选中该框时,文本没有任何反应。
import 'bootstrap/dist/css/bootstrap.min.css';
import React, { useRef, useState } from 'react';
import Form from 'react-bootstrap/Form';
import FormCheck from 'react-bootstrap/FormCheck';
import './App.css';
function App() {
const [state, setState] = useState({ labelChecked: false });
const labelRef = useRef();
const handleClick = event => {
labelRef.current = event.target;
if (state.labelChecked === false) {
labelRef.current.style.textDecorationLine = 'line-through';
} else {
labelRef.current.style.textDecorationLine = 'none';
}
setState({ labelChecked: !state.labelChecked });
};
return (
<div className="App">
<Form style={{ margin: 10 }}>
{['Todo 1', 'Todo 2', 'Todo 3'].map((todo, index) => {
return (
<Form.Check type="checkbox" id={todo} key={index} ref={labelRef}>
<FormCheck.Input type="checkbox" onChange={handleClick} />
<FormCheck.Label>{todo}</FormCheck.Label>
</Form.Check>
);
})}
</Form>
</div>
);
}
export default App;
谢谢!
一个直接的解决方案是将您的检查状态移动到项目级别并且根本不使用引用。使用受控组件是一种很好的做法,这样您的删除线就不会与复选框的本机 DOM 状态分离。
import "bootstrap/dist/css/bootstrap.min.css";
import React, { useState } from "react";
import Form from "react-bootstrap/Form";
import FormCheck from "react-bootstrap/FormCheck";
import "./App.css";
const FormCheckBox = ({ item, index }) => {
const [checked, setChecked] = useState(false);
const styles = { textDecorationLine: checked ? "line-through" : "none" };
const toggleCheck = () => setChecked(!checked);
return (
<Form.Check type="checkbox" id={item}>
<FormCheck.Input type="checkbox" onChange={toggleCheck} value={checked} />
<FormCheck.Label style={styles}>{item}</FormCheck.Label>
</Form.Check>
);
};
function App() {
return (
<div className="App">
<Form style={{ margin: 10 }}>
{["Todo 1", "Todo 2", "Todo 3"].map((todo, index) => {
return <FormCheckBox key={index} item={todo} index={index} />;
})}
</Form>
</div>
);
}
export default App;
在 React 设计系统中,将映射项视为具有
({item, index})
接口的组件是 React 设计系统中的常见模式。这允许您在每个项目中使用挂钩。
为了获取所有复选框的状态,您可以将选中的状态向上移动到 App 并将其维护为当前值的映射或数组。
当前代码的问题是您对所有复选框使用相同的 ref (labelRef)。当您更新 handleClick 函数内的 labelRef.current 值时,它会针对所有复选框进行更新,从而导致意外行为。
要解决此问题,您可以使用 useRef([]) 创建一个引用数组,并为每个复选框存储一个引用。然后在handleClick函数中,可以使用被点击的checkbox的index来更新对应的ref。
这是您的代码的更新版本,应该可以按预期工作:
import 'bootstrap/dist/css/bootstrap.min.css';
import React, { useRef, useState } from 'react';
import Form from 'react-bootstrap/Form';
import FormCheck from 'react-bootstrap/FormCheck';
import './App.css';
function App() {
const [state, setState] = useState({ checkedItems: [] });
const labelRefs = useRef([]);
const handleClick = index => {
const checkedItems = [...state.checkedItems];
checkedItems[index] = !checkedItems[index];
setState({ checkedItems });
const labelRef = labelRefs.current[index];
if (checkedItems[index]) {
labelRef.style.textDecorationLine = 'line-through';
} else {
labelRef.style.textDecorationLine = 'none';
}
};
return (
<div className="App">
<Form style={{ margin: 10 }}>
{['Todo 1', 'Todo 2', 'Todo 3'].map((todo, index) => {
return (
<Form.Check type="checkbox" id={todo} key={index}>
<FormCheck.Input type="checkbox" onChange={() => handleClick(index)} />
<FormCheck.Label ref={el => (labelRefs.current[index] = el)}>{todo}</FormCheck.Label>
</Form.Check>
);
})}
</Form>
</div>
);
}
export default App;