![](/img/trans.png)
[英]Extracting the ref from the forwardRef (createRef compared to useRef) | react native
[英]react cannot read properties of null of ref with forwardRef and useRef
我正在使用 function 組件。 我使用useRef
來存儲previousValueCount
因為我不希望它在更新時重新呈現組件。 只要我的useRef
聲明位於我的代碼使用該 ref in 讀取和寫入的組件中,這就完美了。 然而,意識到我可能需要這個更高的樹,我轉向道具。 但是,當我將useRef
聲明移動到父級並將 ref 作為道具傳遞時,代碼似乎中斷了,告訴我它是 null,不管初始化值如何,它之前在子級中工作。 據我了解,我無法將 ref 作為道具傳遞,所以我轉向React.forwardRef
。 我嘗試了同樣的事情但沒有解決方案,並且在下面分享了一個類似的錯誤。 如何將 ref 作為 prop 或向下組件樹傳遞?
應用程序組件:
function App() {
const [itemsLeftCount, setItemsleftCount] = useState(0);
return (
<ToDos
itemsLeftCount={itemsLeftCount}
setItemsLeftCount={setItemsLeftCount}
></ToDos>
)
}
父組件:
function ToDos(props) {
const prevItemsLeftCountRef = useRef(0);
return (
<ToDoBox
itemsLeftCount={props.itemsLeftCount}
setItemsLeftCount={props.setItemsLeftCount}
ref={prevItemsLeftCountRef}
></ToDoBox>
{
兒童組件:
const ToDoBox = React.forwardRef((props, ref) => {
useEffect(() => {
//LINE 25 Error points to right below comment
ref.prevItemsLeftCountRef.current = props.itemsLeftCount;
}, [props.itemsLeftCount]);
useEffect(() => {
//ON MOUNT
props.setItemsLeftCount(ref.prevItemsLeftCountRef.current + 1);
//ON UNMOUNT
return function removeToDoFromItemsLeft() {
//this needs to only run after setitemsleftcount state is for sure done updating
props.setItemsLeftCount(ref.prevItemsLeftCountRef.current - 1);
};
}, []);
})
我收到此錯誤:未捕獲的類型錯誤:無法在 ToDoBox.js:25:1 讀取 null 的屬性(讀取“prevItemsLeftCountRef”)
@Mustafa Walid
function ToDos(props) {
const prevItemsLeftCountRef = useRef(0);
const setPrevItemsLeftCountRef = (val) => {
prevItemsLeftCountRef.current = val;
};
return (
<ToDoBox
itemsLeftCount={props.itemsLeftCount}
setItemsLeftCount={props.setItemsLeftCount}
prevItemsLeftCountRef={prevItemsLeftCountRef}
setPrevItemsLeftCountRef={setPrevItemsLeftCountRef}
></ToDoBox>
)
}
function ToDoBox(props) {
useEffect(() => {
//itemsLeftCount exists further up tree initialized at 0
props.setPrevItemsLeftCountRef(props.itemsLeftCount);
}, [props.itemsLeftCount]);
}
ref
,然后將此 function 傳遞給它的孩子。 然后,孩子將使用此 function 修改父母的ref
const MainComponent = () => {
const myRef = useRef("old Ref");
const changeRef = (val) => {
myRef.current = val;
};
return <SubComponent changeRef={changeRef} />;
};
const SubComponent = ({ changeRef }) => {
changeRef("new Ref");
return <h1>Changed Parent's Ref!</h1>;
};
您不需要任何參考。 你應該使用 React state。 我真的無法准確確定導致雙倍初始遞減的任何特定問題,但是代碼中有很多問題都值得一提。
首先,在ToDoBox
組件中掛載useEffect
鈎子。 我看到您試圖保留和維護兩個項目計數 state 值,當前值和先前值。 這是完全多余的,如代碼所示,可能會導致 state 同步問題。 簡單地說,您應該在此處使用功能性 state 更新來正確訪問上一個項目計數 state 值來增加或減少它。 您可能還需要檢查當前ToDoBox
組件是否不是返回的清理 function 中的“創建框”,以確保創建 todos 的ToDoBox
不會意外更新項目計數。
例子:
useEffect(() => {
// RUNS ON MOUNT
if (!isCreateBox) {
setItemsLeftCount(count => count + 1);
}
return function removeToDoFromItemsLeft() {
if (!isCreateBox) {
setItemsLeftCount(count => count - 1);
}
};
}, []);
僅此一項就足以阻止項目計數雙減問題。
添加和刪除待辦事項的功能都在改變 state。 這就是為什么您必須在 ToDos 中添加輔助state
ToDos
以強制您的應用程序重新渲染並顯示突變。
addToDoToList
這里updatedArr
是對dataArr
state 對象/數組的引用,而 function直接推入數組(突變),然后將相同的對象/數組引用保存回 Z9ED39E2EA9364586B3EZEFA 中。
function addToDoToList() {
let updatedArr = dataArr; // <-- saved reference to state
updatedArr.push(dataInput); // <-- mutation!!
setDataArr(updatedArr); // <-- same reference back into state
}
function handleClickCreateButton() {
addToDoToList(); // <-- mutates state
//force a rerender of todos
setState({}); // <-- force rerender to see mutation
}
handleClickDeleteButton
同樣,這里的updateArr
是對當前 state 的引用。 Array.prototype.splice
就地改變數組。 再一次,變異的 state 引用被保存回 state 並且需要強制重新渲染。
function handleClickDeleteButton() {
// if splice from dataArr that renders a component todobox for each piece of data
// if remove data from dataArr, component removed...
let updatedArr = dataArr; // <-- saved reference to state
updatedArr.splice(index, 1); // <-- mutation!!
setDataArr(updatedArr); // <-- same reference back into state
// then need to rerender todos... I have "decoy state" to help with that
setState({});
}
奇怪的是,圍繞此代碼的注釋意味着您甚至知道/理解發生了什么事。
這里的解決方案是再次使用功能 state 更新來正確更新之前的 state並同時返回新的對象/數組引用,因此 React 會看到 state 更新並觸發了 rerender 我建議還向待辦事項添加一個id
GUID,以便更輕松地識別它們。 這比使用數組索引要好。
例子:
import { nanoid } from 'nanoid';
...
function addTodo() {
// concat to and return a new array reference
setDataArr(data => data.concat({
id: nanoid(),
todo: dataInput,
}));
}
function removeTodo(id) {
// filter returns a new array reference
setDataArr(data => data.filter(todo => todo.id !== id));
}
itemsLeftCount
state 和setItemsLeftCount
state 更新程序 function 一直到葉子ToDoBox
組件。dataArr
state 不變量的控制。這是您的代碼的簡化/縮小的工作版本:
應用程序
function App() {
const [itemsLeftCount, setItemsLeftCount] = useState(0);
return (
<div>
<ToDos setItemsLeftCount={setItemsLeftCount} />
<div>{itemsLeftCount} items left</div>
</div>
);
}
待辦事項
import { nanoid } from "nanoid";
function ToDos({ setItemsLeftCount }) {
const [dataInput, setInputData] = useState("");
const [dataArr, setDataArr] = useState([]);
useEffect(() => {
// Todos updated, update item count in parent
setItemsLeftCount(dataArr.length);
}, [dataArr, setItemsLeftCount]);
function getInputData(event) {
setInputData(event.target.value);
}
function addTodo() {
setDataArr((data) =>
data.concat({
id: nanoid(),
todo: dataInput
})
);
}
function removeTodo(id) {
setDataArr((data) => data.filter((todo) => todo.id !== id));
}
return (
<div>
<ToDoBox isCreateBox getInputData={getInputData} addTodo={addTodo} />
{dataArr.map((data) => (
<ToDoBox key={data.id} data={data} removeTodo={removeTodo} />
))}
</div>
);
}
待辦事項框
function ToDoBox({ addTodo, data, getInputData, isCreateBox, removeTodo }) {
if (isCreateBox) {
return (
<div>
<input
placeholder="Create a new todo..."
onChange={getInputData}
tabIndex={-1}
/>
<span onClick={addTodo}>+</span>
</div>
);
} else {
return (
<div>
<span>{data.todo}</span>{" "}
<span onClick={() => removeTodo(data.id)}>X</span>
</div>
);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.