简体   繁体   中英

How to update same redux state on two different components simultaneously?

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?


EDIT:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM