首页 > 解决方案 > 如何将此创建商店转换为基于承诺的?

问题描述

我的应用程序最初是从本地存储加载数据,现在我正在尝试使用 firebase。Firebase 总是倾向于返回一个承诺。所以我正在尝试将商店转换为 firebase 返回一个。

这是原来的

export const loadState = () => {
  const state: AppState = getDefaultState();
  VALID_LABS.forEach(labId => {
    state.labs[labId] = getDefaultLabState();
    STORAGE_CONFIG.forEach(storageField => {
      const { statePath, storageKey, defaultValueFn } = storageField;
      const loadedValue = getFromLocalStorage(
        labId,
        storageKey,
        defaultValueFn()
      );
      cachedValues[`${labId}:${storageKey}`] = loadedValue;
      set(state, `labs.${labId}.${statePath}`, loadedValue);
    });
  });
  return state as AppState;
};

const store = createStore(rootReducer, loadState(), applyMiddleware(thunk));
store.subscribe(
  throttle(() => {
    saveState(store.getState());
  }, 500)
);

如您所见,我正在使流程正常工作。但是当我开始使用firebase时出现了问题。

我的 loadState 变成了这样。

export const loadState = (): AppState => {
  if (firebase.auth().currentUser.uid) {
    let userId = firebase.auth().currentUser.uid;
    return firebase
      .database()
      .ref('/users/' + userId)
      .once('value')
      .then(function(snapshot) {
        return snapshot.val() as AppState;
      }).catch((err) => {
        console.error(err)
      })
  }
};

所以我还需要将 转换store为接受promise从新返回的loadState. 我不知道如何转换它,因为我也在使用applyMiddleWare(thunk)

let saveState: (state: AppState) => void
    ;
let loadState: () => AppState;
if(firebase.auth().currentUser){
  loadState = loadStateFirebase;
  saveState = saveStateFirebase;
}else{
  loadState = loadStateLocalStorage;
  saveState = saveStateLocalStorage;
}

// call loadstate then data,pass it in as second para to appstate store
const store = createStore(rootReducer, loadState(), applyMiddleware(thunk));
store.subscribe(
  throttle(() => {
    saveState(store.getState());
  }, 500)
);

有人能帮我吗

标签: reactjstypescriptfirebasefirebase-realtime-database

解决方案


首先,有一个小错误,你可能应该throw err在你的 catch 块的末尾,如果你不这样做,错误将被视为已处理,并且返回的任何内容都catch将被视为 promise 的值。在这种情况下它是undefined,所以如果发生错误,loadState将解析为 undefined 而不是AppStateobject,这可能会导致一些问题,但这取决于你,也许你有其他计划如何处理这个

无论如何,promise 的问题在于,只要一个函数开始使用它,其他所有函数都必须使用它。更简单的选择是在异步函数中创建存储:

function createStore() {
  return loadState()
    .then(state => {
      const store = createStore(rootReducer, state, applyMiddleware(thunk))
      // Either subscribe to store here
      return store
    })
}

const storePromise = createStore()
// or here
/* storePromise.then(store => store.subscribe(...))

或使用async/await

async function createStore() {
  const state = await loadState()
  return createStore(rootReducer, state, applyMiddleware(thunk))
}
const storePromise = createStore()

很难判断这是否适合您的应用程序,根据标签判断,您正在使用 react,因此您可能需要添加额外的逻辑来加载此商店,例如编写 aStoreProvider并用它包装App

function StoreProvider({ children }: { children?: ReactNode }) {
  const [store, setStore] = useState<Store<AppState> | null>(null)

  useEffect(() => {
    createStore().then(setStore)
  }, [])

  if(!store) return <Loading /> // or whatever
  return <Provider store={store}>{children}</Provider>
}

这对你来说可能没问题,也可能不行。通常这不是很好,因为您的应用程序在firebase加载其内容时无法渲染。另一种解决方案是在开始时使用空状态初始化 store,渲染应用程序中不需要 firebase 人员的部分并让其他人等待它加载,您可以thunk为此使用 s 或创建三个操作LOAD_STORE/REQUEST,LOAD_STORE/SUCCESSLOAD_STORE/ERRORmake您的应用程序以适当的方式呈现。您也许可以创建一个系统,在加载时将一些需要存储的操作排队,并在完成后立即自动执行它们,这里有很多创意和挫折的空间,设计异步存储是一个相当大的挑战,您需要根据您的应用程序需求决定您想如何做


推荐阅读