React redux, Uncaught TypeError: Cannot assign to read only property 'current' of object '#<Object>'
React redux, Uncaught TypeError: Cannot assign to read only property 'current' of object '#<Object>'
我正在制作一个修改数据库数据的网站。
一、组件结构如下
<Contents />
<Table />
<Row />
<Column />
<Input />
创建行组件时,创建输入组件的ref并由redux管理。
const StyledRow = styled.div`
text-align:center;
display:flex;
align-items:center;
`;
const DeleteButton = styled(Button)`
background-color: #ff7787;
margin-right:5px;
color:white;
width:40px;
${({top}) => top && css`
background-color:white;
color:white;
width:40px;
`}
`;
function Row({top, rowId}){
const dispatch = useDispatch();
const columns = useMemo(() => columnPhoneInfo,[]);
const inputsRef = useMemo(()=>
!top &&
Array(8).fill(0).map(() => createRef()
),[]);
// const inputsRef = useRef([]);
useEffect(()=> {
// console.log(rowId,top);
!top &&
dispatch(phoneDataAddRef(rowId,inputsRef));
},[]);
const handleDeleteButton = useCallback( (id) => {
dispatch(phoneDataUpdate.Delete(id));
},[]);
if( top ) return(
<StyledRow>
<DeleteButton top/>
{columns.map((column)=>
<Column key={`head_${column.name}`} width={column.width} top>
{column.name}
</Column>
)}
</StyledRow>
);
return(
<StyledRow>
<DeleteButton onClick={()=>handleDeleteButton(rowId)}> delete </DeleteButton>
{columns.map((column, index)=>
<Column key={`row_${rowId}_${column.name}`} width={column.width} textalign={column.textalign}>
<Input ref={inputsRef[index] } colIndex={index} id={rowId} column={column} />
{/* <Input colIndex={index} id={rowId} column={column} /> */}
</Column>
)}
</StyledRow>
);
}
export default React.memo(Row);
输入组件只接收 ref 作为 forwardRef
const StyledInput = styled.input`
${({ width, textalign })=>css`
width:${width};
text-align:${textalign};
`}
`;
const Input = forwardRef(({colIndex, id},inputRef) =>{
const dispatch = useDispatch();
const didShowAlert = useRef(false);
const nowColumnInfo = columnPhoneInfo[colIndex];
const nowColumnValidCheck = inputValidCheck[colIndex];
const { nowVal, firstVal, isAddedRow } = useSelector(state =>({
nowVal : state.phoneData.data.rows.find(val=>val.id === id)[nowColumnInfo.colname],
firstVal : state.phoneData.firstData.lastId < id
? null
: state.phoneData.firstData.rows.find(val=>val.id===id)[nowColumnInfo.colname],
isAddedRow : state.phoneData.firstData.lastId < id
? true
: false,
}),shallowEqual);
const callbackDispatch = useCallback((dispatchFunc) =>{
return(...args)=>{
dispatch(dispatchFunc(...args));
}
},[dispatch]);
//////////////////////
const inputChange = useCallback( (value) =>
dispatch(phoneDataUpdate.Change(id,nowColumnInfo.colname, value))
,[nowColumnInfo.colname, dispatch, id]);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const updateListChange = callbackDispatch(phoneDataUpdateList.Change);
const updateListDelete = callbackDispatch(phoneDataUpdateList.Delete);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const handleChange = useCallback( (e) => {
// ... todo handle change
},[]);
/////////////////////////////////////////////////////////
const handleBlur = useCallback( (e) =>{
// ... todo handle blur
},[]);
return(
<StyledInput
textalign={nowColumnInfo.textalign}
width={nowColumnInfo.width}
value={nowVal === null ? '': nowVal }
onChange={handleChange}
onBlur={handleBlur}
ref={inputRef}
// placeholder={}
/>
);
});
export default React.memo(Input);
最后,redux 模块
////////////////////////////////////////////////////////
const PHONE_DATA_DELETE = 'phoneData/PHONE_DATA_DELETE';
////////////////////////////////////////////////////////
const PHONE_DATA_ADD_REF = 'phoneData/PHONE_DATA_ADD_REF';
////////////////////////////////////////////////////////
const dataInitRow = {
id:null,
model_name:null,
machine_name:null,
shipping_price:null,
maker:null,
created:null,
battery:null,
screen_size:null,
storage:null,
};
const dataInit = {
lastId:null,
rows:[],
}
const initialState = {
state:{
loading:false,
error:false,
},
data:dataInit,
refData:[],
firstData:dataInit,
dataChangeList:{
dataAddList:[],
dataDeleteList:[],
dataUpdateList:[],
},
};
const phoneDataFetchAsync = createPromiseThunk(PHONE_DATA, restAPI.getAllPhoneInfo);
////////////////////////////////////////////////////////
const phoneDataAddRef=(id, ref) =>({
type:PHONE_DATA_ADD_REF,
id:id,
ref:ref,
});
const phoneDataUpdateList = ({
Change:(id,colName, value) => ({
type:PHONE_DATA_UPDATE_LIST_CHANGE,
id: id,
colName: colName,
value: value,
}),
Delete:(id, colName) => ({
type:PHONE_DATA_UPDATE_LIST_DELETE,
id: id,
}),
});
////////////////////////////////////////////////////////
export default function phoneData(state = initialState, action){
// console.log(`add: ${state.dataChangeList.dataAddList}, delete: ${state.dataChangeList.dataDeleteList}, change: ${state.dataChangeList.dataUpdateList}`);
switch(action.type)
case PHONE_DATA_DELETE:
return produce(state, draft=>{
console.log(action);
const idx = state.dataChangeList.dataAddList.findIndex( val => val === action.id);
if( idx === -1 )
draft.dataChangeList.dataDeleteList.push(action.id);
else
draft.dataChangeList.dataAddList.splice(idx,1);
draft.refData = state.refData.filter(row => row.id !== action.id);
draft.data.rows = state.data.rows.filter(row =>row.id !== action.id);
});
////////////////////////////////////////////////////////////////////////////////////////////////////////////
case PHONE_DATA_ADD_REF:
return produce(state, draft=>{
draft.refData.push({id:action.id, refs:action.ref});
});
////////////////////////////////////////////////////////////////////////////////////////////////////////////
default:
return state;
}
}
export {phoneDataFetchAsync,
phoneDataDelete,,
phoneDataAddRef,
};
问题区域是删除按钮。当我按下按钮时,出现该错误。
但是,如果不将 ref 添加到状态,则不会发生错误。或者即使我注释掉底部,也没有错误。
draft.data.rows = state.data.rows.filter(row =>row.id !== action.id);
或者把下面的部分注释掉
draft.refData.push({id:action.id, refs:action.ref});
我今天一整天都在尝试修复它,但我不知道哪里出了问题。我该如何解决?
不要在 Redux 中存储引用
您在这里尝试做的事情不仅违反了“基本原则”中的一项,还违反了两项rules of Redux。
Ref 对象在设计上是可变的。通过更新 ref 对象的 .current
属性 来更改引用,但对象实例保持不变。这违反了状态必须不可变的 Redux 规则,因为 Redux 状态可以被不相关的 React 代码改变。
Redux 存储中的所有数据都应该能够转换为 JSON string
并返回。任何在转换中丢失的东西都不属于 Redux。 DOM 元素的实例不可序列化。
您需要弄清楚需要哪些原始数据来表示您的状态。将该数据存储在 Redux 中并使用它来生成正确的 DOM 元素。在 Redux 中存储 DOM 是倒退的。
我正在制作一个修改数据库数据的网站。 一、组件结构如下
<Contents />
<Table />
<Row />
<Column />
<Input />
创建行组件时,创建输入组件的ref并由redux管理。
const StyledRow = styled.div`
text-align:center;
display:flex;
align-items:center;
`;
const DeleteButton = styled(Button)`
background-color: #ff7787;
margin-right:5px;
color:white;
width:40px;
${({top}) => top && css`
background-color:white;
color:white;
width:40px;
`}
`;
function Row({top, rowId}){
const dispatch = useDispatch();
const columns = useMemo(() => columnPhoneInfo,[]);
const inputsRef = useMemo(()=>
!top &&
Array(8).fill(0).map(() => createRef()
),[]);
// const inputsRef = useRef([]);
useEffect(()=> {
// console.log(rowId,top);
!top &&
dispatch(phoneDataAddRef(rowId,inputsRef));
},[]);
const handleDeleteButton = useCallback( (id) => {
dispatch(phoneDataUpdate.Delete(id));
},[]);
if( top ) return(
<StyledRow>
<DeleteButton top/>
{columns.map((column)=>
<Column key={`head_${column.name}`} width={column.width} top>
{column.name}
</Column>
)}
</StyledRow>
);
return(
<StyledRow>
<DeleteButton onClick={()=>handleDeleteButton(rowId)}> delete </DeleteButton>
{columns.map((column, index)=>
<Column key={`row_${rowId}_${column.name}`} width={column.width} textalign={column.textalign}>
<Input ref={inputsRef[index] } colIndex={index} id={rowId} column={column} />
{/* <Input colIndex={index} id={rowId} column={column} /> */}
</Column>
)}
</StyledRow>
);
}
export default React.memo(Row);
输入组件只接收 ref 作为 forwardRef
const StyledInput = styled.input`
${({ width, textalign })=>css`
width:${width};
text-align:${textalign};
`}
`;
const Input = forwardRef(({colIndex, id},inputRef) =>{
const dispatch = useDispatch();
const didShowAlert = useRef(false);
const nowColumnInfo = columnPhoneInfo[colIndex];
const nowColumnValidCheck = inputValidCheck[colIndex];
const { nowVal, firstVal, isAddedRow } = useSelector(state =>({
nowVal : state.phoneData.data.rows.find(val=>val.id === id)[nowColumnInfo.colname],
firstVal : state.phoneData.firstData.lastId < id
? null
: state.phoneData.firstData.rows.find(val=>val.id===id)[nowColumnInfo.colname],
isAddedRow : state.phoneData.firstData.lastId < id
? true
: false,
}),shallowEqual);
const callbackDispatch = useCallback((dispatchFunc) =>{
return(...args)=>{
dispatch(dispatchFunc(...args));
}
},[dispatch]);
//////////////////////
const inputChange = useCallback( (value) =>
dispatch(phoneDataUpdate.Change(id,nowColumnInfo.colname, value))
,[nowColumnInfo.colname, dispatch, id]);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const updateListChange = callbackDispatch(phoneDataUpdateList.Change);
const updateListDelete = callbackDispatch(phoneDataUpdateList.Delete);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const handleChange = useCallback( (e) => {
// ... todo handle change
},[]);
/////////////////////////////////////////////////////////
const handleBlur = useCallback( (e) =>{
// ... todo handle blur
},[]);
return(
<StyledInput
textalign={nowColumnInfo.textalign}
width={nowColumnInfo.width}
value={nowVal === null ? '': nowVal }
onChange={handleChange}
onBlur={handleBlur}
ref={inputRef}
// placeholder={}
/>
);
});
export default React.memo(Input);
最后,redux 模块
////////////////////////////////////////////////////////
const PHONE_DATA_DELETE = 'phoneData/PHONE_DATA_DELETE';
////////////////////////////////////////////////////////
const PHONE_DATA_ADD_REF = 'phoneData/PHONE_DATA_ADD_REF';
////////////////////////////////////////////////////////
const dataInitRow = {
id:null,
model_name:null,
machine_name:null,
shipping_price:null,
maker:null,
created:null,
battery:null,
screen_size:null,
storage:null,
};
const dataInit = {
lastId:null,
rows:[],
}
const initialState = {
state:{
loading:false,
error:false,
},
data:dataInit,
refData:[],
firstData:dataInit,
dataChangeList:{
dataAddList:[],
dataDeleteList:[],
dataUpdateList:[],
},
};
const phoneDataFetchAsync = createPromiseThunk(PHONE_DATA, restAPI.getAllPhoneInfo);
////////////////////////////////////////////////////////
const phoneDataAddRef=(id, ref) =>({
type:PHONE_DATA_ADD_REF,
id:id,
ref:ref,
});
const phoneDataUpdateList = ({
Change:(id,colName, value) => ({
type:PHONE_DATA_UPDATE_LIST_CHANGE,
id: id,
colName: colName,
value: value,
}),
Delete:(id, colName) => ({
type:PHONE_DATA_UPDATE_LIST_DELETE,
id: id,
}),
});
////////////////////////////////////////////////////////
export default function phoneData(state = initialState, action){
// console.log(`add: ${state.dataChangeList.dataAddList}, delete: ${state.dataChangeList.dataDeleteList}, change: ${state.dataChangeList.dataUpdateList}`);
switch(action.type)
case PHONE_DATA_DELETE:
return produce(state, draft=>{
console.log(action);
const idx = state.dataChangeList.dataAddList.findIndex( val => val === action.id);
if( idx === -1 )
draft.dataChangeList.dataDeleteList.push(action.id);
else
draft.dataChangeList.dataAddList.splice(idx,1);
draft.refData = state.refData.filter(row => row.id !== action.id);
draft.data.rows = state.data.rows.filter(row =>row.id !== action.id);
});
////////////////////////////////////////////////////////////////////////////////////////////////////////////
case PHONE_DATA_ADD_REF:
return produce(state, draft=>{
draft.refData.push({id:action.id, refs:action.ref});
});
////////////////////////////////////////////////////////////////////////////////////////////////////////////
default:
return state;
}
}
export {phoneDataFetchAsync,
phoneDataDelete,,
phoneDataAddRef,
};
问题区域是删除按钮。当我按下按钮时,出现该错误。 但是,如果不将 ref 添加到状态,则不会发生错误。或者即使我注释掉底部,也没有错误。
draft.data.rows = state.data.rows.filter(row =>row.id !== action.id);
或者把下面的部分注释掉
draft.refData.push({id:action.id, refs:action.ref});
我今天一整天都在尝试修复它,但我不知道哪里出了问题。我该如何解决?
不要在 Redux 中存储引用
您在这里尝试做的事情不仅违反了“基本原则”中的一项,还违反了两项rules of Redux。
Ref 对象在设计上是可变的。通过更新 ref 对象的 .current
属性 来更改引用,但对象实例保持不变。这违反了状态必须不可变的 Redux 规则,因为 Redux 状态可以被不相关的 React 代码改变。
Redux 存储中的所有数据都应该能够转换为 JSON string
并返回。任何在转换中丢失的东西都不属于 Redux。 DOM 元素的实例不可序列化。
您需要弄清楚需要哪些原始数据来表示您的状态。将该数据存储在 Redux 中并使用它来生成正确的 DOM 元素。在 Redux 中存储 DOM 是倒退的。