简体   繁体   中英

How to integrate redux inside a react.js app?

while I'm trying to apply redux_react with functional code I have a problem with.subscribe() it runs multiple times every state update = (count of all previous updates + 1) how can I re-render the UI only once.. every state update

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- Redux -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.0/redux.min.js" integrity="sha512-1/8Tj23BRrWnKZXeBruk6wTnsMJbi/lJsk9bsRgVwb6j5q39n0A00gFjbCTaDo5l5XrPVv4DZXftrJExhRF/Ug==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <!-- react & react-dom -->
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <!-- Babel: transpiles JSX code into Common JS - that browser can understand -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>

    <div id="app"></div>

    <!-- Babel JSX code -->
    <script type="text/babel">

        // =============================== Redux (State management) =============================================

        // action types
        const [ADD_TODO, REMOVE_TODO, TOGGLE_TODO] = ["ADD_TODO", "REMOVE_TODO", "TOGGLE_TODO"]
        const [ADD_GOAL] = ["ADD_GOAL"]

        // action creators
        const addTodo = todo => ({
            type: ADD_TODO,
            todo
        })
        const removeTodo = id => ({
            type: REMOVE_TODO,
            id
        })
        const toggleTodo = id => ({
            type: TOGGLE_TODO,
            id
        })
        const addGoal = goal => ({
            type: ADD_GOAL,
            goal
        })

        // Reducers
        const todosReducer = (state=[], action) => {
            switch (action.type) {
                case ADD_TODO:
                    return [...state, action.todo]
                case REMOVE_TODO:
                    return state.filter((s) => s.id !== action.id)
                case TOGGLE_TODO:
                    return state.map((s) => (s.id == action.id)? {...s, complete: !s.complete} : s)
                default:
                    return state
            }
        }
        const goalsReducer = (state=[], action) => {
            switch (action.type) {
                case ADD_GOAL:
                    return [...state, action.goal]
                default:
                    return state
            }
        }


        // Redux Store
        const store = Redux.createStore(
            // Root Reducer
            Redux.combineReducers({
                todos: todosReducer,
                goals: goalsReducer
            })
        )





        // =============================== React App =============================================

        // Components
        const List = ({items, removeItem, toggle}) => {
            return (
                <ul>
                    {
                        items.map((itm) =>
                            <li key={itm.id}>
                                <span id={itm.id} onClick={(toggle)? toggle : null}  style={{textDecoration: (itm.complete)? 'line-through' : ''}}>
                                    {itm.text}
                                </span>
                                <button id={itm.id} onClick={removeItem}> X </button>
                            </li>
                        )
                    }
                </ul>
            )
        }

        const Todos = ({items}) => {

            const newTodoInput = React.useRef()

            // add new
            const handleAddTodo = () => {
                const newTodo = newTodoInput.current

                // Redux <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                // dispatch the addTodo action --> to update Redux State
                // ** No need to lift-up handleAddTodo() to all parents :)
                store.dispatch(
                    addTodo({
                        id: (new Date()).getTime().toString(36),
                        text: newTodo.value,
                        complete: false
                    })
                )

                newTodo.value = ""
            }

            // remove
            const handleRemoveTodo = (e) => {
                store.dispatch(removeTodo(e.target.id))
            }

            // toggle
            const handleToggle = (e) => {
                store.dispatch(toggleTodo(e.target.id))
            }

            return (
                <div>
                    <h3>My Todos:</h3>
                    <div>
                        <input type="text" placeholder="new todo ..." ref={newTodoInput} />
                        <button onClick={handleAddTodo}>Save</button>
                    </div>
                    <List items={items} removeItem={handleRemoveTodo} toggle={handleToggle} />
                </div>
            )
        }

        const Goals = ({items}) => {
            return (
                <div>
                    <h3>My Goals:</h3>
                    <List items={items} />
                </div>
            )
        }

        const App = ({store}) => {
            const [todos, setTodos] = React.useState([])
            const [goals, setGoals] = React.useState([])

            // Redux <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            store.subscribe(() => {
                console.log(store.getState())

                const newState = store.getState()
                setTodos(newState.todos)
                setGoals(newState.goals)
            })

            return (
                <div>
                    <Todos items={todos} />
                    <Goals items={goals} />
                </div>
            )
        }


        // Root
        const root = ReactDOM.createRoot(document.getElementById("app"))

        root.render(
            <App store={store} />
        )

    </script>
</body>
</html>

how can i make the state updates without subscribing on all previous updates

You can avoid to use subscribe with react, here is the recommended way:

You can use redux with react hooks to avoid using subscribe

To track redux update when state change you can use useSelector which is made for

Then you avoid to use subscribe to your state change, useSelector do it for you

Do not forget to use Provider from react-redux

Here is a small example

      const Goals = () => {
            const items = useSelector(goalsSelector)
            return (
                <div>
                    <h3>My Goals:</h3>
                    <List items={items} />
                </div>
            )
        }

        const App = ({store}) => {
            return (
              <Provider store={store}>
                <div>
                    <Todos />
                    <Goals />
                </div>
              </Provider>
            )
        }

If you still want to subscribe inside a react function component you must use useEffect

With your current implementation on every renderer you subscribe again to redux.

Here you subscribe only when the component is mounted then you unsubscribe when you unmount it

       const App = ({store}) => {
            const [todos, setTodos] = React.useState([])
            const [goals, setGoals] = React.useState([])

            // Redux <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            React.useEffect(() => {
              const unsubscribe = store.subscribe(() => {
                console.log(store.getState())

                const newState = store.getState()
                setTodos(newState.todos)
                setGoals(newState.goals)
              })
              return unsubscribe;
            }, [])

            return (
                <div>
                    <Todos items={todos} />
                    <Goals items={goals} />
                </div>
            )
        }

Use useSelector or the connect() API to read from redux state instead of subscribing to the store manually. useSelector and useDispatch are the relevant hooks to dispatch actions and access state inside function components.

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