简体   繁体   English

如何在 react.js 应用程序中集成 redux?

[英]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当我尝试将 redux_react 与功能代码一起应用时,我遇到了 .subscribe() 的问题,它每次 state 更新 =(所有先前更新的计数 + 1)运行多次state 更新

<!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如何在不订阅所有先前更新的情况下进行 state 更新

You can avoid to use subscribe with react, here is the recommended way:你可以避免使用 subscribe 和 react,这里是推荐的方式:

You can use redux with react hooks to avoid using subscribe您可以将 redux 与反应挂钩使用以避免使用subscribe

To track redux update when state change you can use useSelector which is made for要在 state 更改时跟踪useSelector更新,您可以使用为

Then you avoid to use subscribe to your state change, useSelector do it for you然后你避免使用subscribe你的 state 更改,使用选择器为你做

Do not forget to use Provider from react-redux不要忘记使用react-redux中的Provider

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如果您仍想在 react function 组件中订阅,则必须使用useEffect

With your current implementation on every renderer you subscribe again to redux.使用您当前在每个渲染器上的实现,您再次订阅 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.使用useSelectorconnect() API 从 redux state 读取,而不是手动订阅商店。 useSelector and useDispatch are the relevant hooks to dispatch actions and access state inside function components. useSelectoruseDispatch是派发动作和访问 function 组件中的 state 的相关钩子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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