I have a React Native scenario in which I have two components, one is a textInput
and the other is a SVG
drawing. By filling out the textInput
and pressing a button, the text is inserted as a data instance to an array stored in a redux state using a dispatch
action (let's call it textListState
).
//TEXT INPUT COMPONENT
//this code is just for presentation. it will cause errors obviously
const dispatch = useDispatch();
const submitHandler = (enteredText) => {
dispatch(submitData(enteredText))
};
return(
<TextInput ...... />
<TouchableOpacity onPress={() => submitHandler(enteredText) } />
)
Now, the SVG
component updates all the features' fill color stored in the textListState
using a function (eg setFillColor
). This is done inside a useEffect
hook with a dependency set to a prop (eg propA
).
Now the problem is that I need to add textListState
as a dependency to the useEffect
because I want the newly entered text from the textInput
to be included in the SVG
component. But by doing so, I am creating an infinite loop, because the setFillColor
also updates the textListState
.
//SVG COMPONENT
const [textList, setTextList] = useState([]);
const textListState = useSelector(state => state.textListState);
const dispatch = useDispatch();
const setFillColor= useCallback((id, fill) => {
dispatch(updateFillColor(id, fill))
}, [dispatch]);
useEffect(() => {
//INFINITE LOOP BECAUSE textListState KEEPS UPDATING WITH setFillColor
const curTextList = [];
for (const [i, v] of textListState.entries()) {
setFillColor(5, "white")
curTextList.push(
<someSVGComponent />
)
}
setTextList(curTextList);
}, [props.propA, textListState])
return(
<G>
{textList.map(x => x)}
</G>
)
How can I achieve to be able to add the newly inserted text to the SVG
component without creating an infinite loop?
The redux action
export const UPDATE_TEXT = "UPDATE_TEXT";
export const SUBMIT_DATA = "SUBMIT_DATA";
export const updateFillColor = (id, fill) => {
return { type: UPDATE_TEXT, id: id, fill: fill }
};
export const submitData= (text) => {
return { type: SUBMIT_DATA, text: text }
};
The redux reducer
import { TEXT } from "../../data/texts";
import { UPDATE_TEXT } from "../actions/userActions";
import { INSERT_TEXT } from "../actions/userActions";
// Initial state when app launches
const initState = {
textListState: TEXT
};
const textReducer = (state = initState, action) => {
switch (action.type) {
case INSERT_TEXT :
return {
...state,
textListState: [
...state.textListState,
action.text
]
}
case UPDATE_TEXT :
const curText = state.textListState.filter(text => text.id === action.id)[0];
return {
...state,
textListState: [
...state.textListState.slice(0, curIndex), //everything before current text
curText.fill = action.fill,
...state.textListState.slice(curIndex + 1), //everything after current text
]
}
default:
return state;
}
};
export default textReducer;
if you want to trigger useEffect only when new text is added then use textListState.length
instead of textListState
in dependency list.
If you want to do it both on update and insert (ie you want to fire even when text is updated in input) then use a boolean flag textUpdated
in textListState
and keep toggling it whenever there is a update or insert and use textListState.textUpdated
in dependency list.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.