【问题标题】:Refactor React code, to use Redux state from a Firebase RT database & Redux Hooks重构 React 代码,以使用 Firebase RT 数据库和 Redux Hooks 中的 Redux 状态
【发布时间】:2021-02-15 19:06:55
【问题描述】:

我一直在使用我以前使用过的样板设置网站/React 应用程序,该样板文件集成了 Redux 和 Firebase 数据库。但是,我第一次在使用此样板的项目中使用 React Hooks。到目前为止,我从 Firebase RT-DB 获取数据并将其与 useState 和 useEffect 一起使用没有问题。我现在想重构我的代码以将我的应用程序的状态存储在 Redux 中,但仍然使用功能组件并利用 Hooks。

我无法将所有这些放在一起。

我的设置目前看起来像这样...我没有验证来自 Firebase 的任何数据,只是直接将数据拉入。我已经将我的主页组件作为示例包含了来自 Firebase 的 2 个基本状态/数据db {firstName} & {lastName}。

为了澄清我的问题,我无法从动作生成器内的实时数据库中获取数据,然后将其分派到 reducer 函数。

我已经包含了我认为动作应该是什么样的,但我不确定这是否正确,然后 reducer 函数应该是什么样的。

任何帮助,不胜感激。

app.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store/configureStore';
import AppRouter, { history } from './routers/AppRouter';

// const store = configureStore();

const jsx = (
  <Provider store={store}>
    <AppRouter history={history} />
  </Provider>

);

ReactDOM.render(jsx, document.getElementById('app')); 

configureStore.js

import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';

export const reducers = combineReducers({

});

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

// store.js

export function configureStore(initialState = {}) {

  const store = createStore(reducers, initialState, composeEnhancers(applyMiddleware(thunk)), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

  return store;
}

export const store = configureStore();

homepage.js 组件示例

import React, { useState, useEffect } from "react";
import database from '../firebase/firebase';

const HomePage = () => {

    const [firstName, setFirstName] = useState(firstName);
    const [lastName, setLastName] = useState(lastName);

    useEffect(() => {
        database.ref()
            .once('value')
            .then((snapshot) => {
                const data = snapshot.val();
                const { firstName, lastName } = data
                setFirstName(firstName)
                setLastName(lastName)
            })
            .catch((e) => {
                console.log('Error fetching data', e);
            });
    }, []);

    return (
         <div className="homepage-section__content">
            <HomePageTitle firstName={firstName} lastName={lastName} />
         </div>
    )
}

export { HomePage as default };

homePageTitle.js 组件

import React from 'react';

const HomePageTitle = (props) => (
    <React.Fragment>
        <h1>
            {props.firstName}
            <span>{props.lastName}</span>
        </h1>
    </React.Fragment>
);

export { HomePageTitle as default };

【问题讨论】:

  • 似乎您可以使用标准操作来分派从数据库中获取的操作,使用 thunk 来处理异步逻辑,并分派成功操作来更新您的 redux 存储。你试过什么?你能包括你的动作和减速器吗?您有什么特别的方面或问题吗?
  • 我还没有尝试过任何东西,因为我不知道该怎么做。我知道有 Redux 钩子、useReducer 和 useSelector,但我不确定如何集成它们。这基本上是我的问题。我想我需要将数据库获取移动到一个动作中,但是我如何将它移动到减速器然后存储,我知道如何去做。
  • react-reduxuseDispatchuseSelector 钩子,useReducer 是一个标准的 React 钩子,与 redux 无关(但应用了非常相似的模式)。如果您已经熟悉react-redux,那么调度一个动作基本上与以前使用“mapDispatchToProps”时相同,但现在它不再是道具,动作创建者完全相同。如果使用钩子仍然有点混乱,您仍然可以使用您可能更熟悉的较旧的connect 高阶组件。从这里我们可以帮助它工作或转换为使用钩子。
  • 是的,我正在尝试使用挂钩。这也是我第一次在 Redux 内部从 firebase 获取数据,所以这对我来说是全新的。任何帮助或指示都会很棒。

标签: reactjs firebase firebase-realtime-database react-redux react-hooks


【解决方案1】:

以下是您可以将本地组件状态和useEffect 回调移植到 redux 状态和操作的方法。

您可以定义一组数据获取启动、成功和失败操作。

const setDataLoading = loading => ({
  type: "FETCH_DATA_LOADING",
  payload: loading,
});

const fetchDataSuccess = payload => ({
  type: "FETCH_DATA_SUCCESS",
  payload, // { firstName, lastName }
});

const fetchDataFailure = () => ({ type: "FETCH_DATA_FAILURE" });

const fetchData = () => dispatch => {
  dispatch(setDataLoading(true)); // <-- start loading
  return database.ref() // <-- return Promise chain
    .once('value')
    .then((snapshot) => {
      const { firstName, lastName } = snapshot.val();
      dispatch(fetchDataSuccess({ firstName, lastName }));
    })
    .catch((e) => {
      console.error('Error fetching data', e);
      dispatch(fetchDataFailure());
    })
    .finally(() => dispatch(setDataLoading(false))); // <-- complete loading
};

定义你的减速器。 FETCH_DATA 操作启动异步操作以获取数据,清除所有当前错误状态,并将 loading 设置为 true。在成功或失败时,加载状态被清除,名称数据被存储或错误值被设置。

const initialState = {
  error: false,
  loading: false,
  firstName: null,
  lastName: null,
};

const reducer = (state = initialState, action) => {
  switch(action.type) {
    case "FETCH_DATA_LOADING":
      return {
        ...state,
        loading: action.payload,
      };

    case "FETCH_DATA_SUCCESS":
      return {
        ...state,
        error: false,
        ...action.payload, // ...{ firstName, lastName }
      };

    case "FETCH_DATA_FAILURE":
      return {
        ...state,
        error: true,
      };

    default:
      return state;
  }
};

添加reducer到store。

import userReducer from './path/to/reducer/above';

const reducers = combineReducers({
  user: userReducer,
});

HomePage 连接到 redux 存储。

const HomePage = () => {
  const dispatch = useDispatch();
  const { error, firstName, lastName, loading } = useSelector(state => state.user);

  useEffect(() => {
    dispatch(fetchData());
  }, []);

  return (
    <div className="homepage-section__content">
      {error && <div>Error fetching user name</div>}
      {loading ? (
        <LoadingSpinner />
      ) : (
        <HomePageTitle firstName={firstName} lastName={lastName} />
      )}
    </div>
  )
};

【讨论】:

  • 这很完美,解释也很棒!
猜你喜欢
  • 2019-01-06
  • 1970-01-01
  • 2021-08-27
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 2020-01-30
  • 1970-01-01
  • 2021-11-01
相关资源
最近更新 更多