繁体   English   中英

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

[英]How to integrate redux inside a react.js app?

当我尝试将 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>

如何在不订阅所有先前更新的情况下进行 state 更新

你可以避免使用 subscribe 和 react,这里是推荐的方式:

您可以将 redux 与反应挂钩使用以避免使用subscribe

要在 state 更改时跟踪useSelector更新,您可以使用为

然后你避免使用subscribe你的 state 更改,使用选择器为你做

不要忘记使用react-redux中的Provider

这是一个小例子

      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>
            )
        }

如果您仍想在 react function 组件中订阅,则必须使用useEffect

使用您当前在每个渲染器上的实现,您再次订阅 redux。

在这里,您仅在安装组件时订阅,然后在卸载组件时取消订阅

       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>
            )
        }

使用useSelectorconnect() API 从 redux state 读取,而不是手动订阅商店。 useSelectoruseDispatch是派发动作和访问 function 组件中的 state 的相关钩子。

暂无
暂无

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

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