@yunu-lab/rpc-react
Version:
🚀 **React + RPC = ❤️**
336 lines (335 loc) • 9.88 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const toolkit = require("@reduxjs/toolkit");
const jsxRuntime = require("react/jsx-runtime");
const React = require("react");
const reactRedux = require("react-redux");
const rpcTs = require("@yunu-lab/rpc-ts");
const extendStore = (options) => {
const { repository, store, slices = {} } = options;
const rpcSlice = toolkit.createSlice({
name: "rpc",
initialState: {},
reducers: {
setData: (state, action) => {
const { type, data } = action.payload;
state[type] = data;
}
}
});
const newReducer = toolkit.combineReducers({
...slices,
rpc: rpcSlice.reducer
});
store.replaceReducer(newReducer);
const unsubscribe = repository.onDataChanged((events) => {
events.forEach((event) => {
console.log(
event,
"--------------------------------events-----------------------"
);
const { type, payload } = event;
const storageType = repository.getStorageType(String(type));
if (storageType === "singleton") {
store.dispatch(
rpcSlice.actions.setData({
type: String(type),
data: payload
})
);
} else {
const dataObject = payload.reduce(
(acc, item) => {
const id = item.id;
acc[id] = item;
return acc;
},
{}
);
store.dispatch(
rpcSlice.actions.setData({
type: String(type),
data: dataObject
})
);
}
});
});
return {
store,
repository,
unsubscribe
};
};
const RpcContext = React.createContext(null);
const RpcProvider = ({
children,
repository
}) => {
return /* @__PURE__ */ jsxRuntime.jsx(RpcContext.Provider, { value: { repository }, children });
};
const useRpc = () => {
const context = React.useContext(RpcContext);
if (!context) {
throw new Error("useRpc must be used within an RpcProvider");
}
const repository = React.useMemo(
() => context.repository,
[context.repository]
);
return {
repository
};
};
const createRpcHooks = (typeKeys) => {
const toPascalCase = (s) => {
return s.split("_").map(
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
).join("");
};
const hooks = {};
typeKeys.forEach((typeName) => {
const hookName = `use${toPascalCase(
String(typeName)
)}`;
const typeKey = typeName;
function useHook(id) {
const { repository } = useRpc();
const allData = reactRedux.useSelector(
(state) => state.rpc[typeKey] || {}
);
const findById = React.useCallback(
(id2) => repository.findById(typeKey, id2),
[repository]
);
const findAll = React.useCallback(
() => repository.findAll(typeKey),
[repository]
);
const mergeRpc = React.useCallback(
(data) => repository.mergeRpc(typeKey, data),
[repository]
);
if (id !== void 0) {
return findById(id);
}
return {
[`${String(typeKey)}s`]: Object.values(allData),
[`${String(typeKey)}Map`]: allData,
findById,
findAll,
mergeRpc
};
}
hooks[hookName] = useHook;
const fullRelatedHookName = `use${toPascalCase(
String(typeName)
)}FullRelatedData`;
function useFullRelatedHook(id) {
const { repository } = useRpc();
const allRpcData = reactRedux.useSelector(
(state) => state.rpc
);
const [fullData, setFullData] = React.useState(null);
const allRpcDataString = React.useMemo(
() => JSON.stringify(allRpcData),
[allRpcData]
);
const getData = React.useCallback(() => {
try {
const result = repository.getFullRelatedData(
typeName,
id
);
setFullData(result);
} catch {
setFullData(null);
}
}, [repository, id]);
React.useEffect(() => {
console.log(
`[${String(
typeName
)}FullRelatedData] Fetching full related data for id:`,
id
);
getData();
}, [getData, allRpcDataString, id]);
return fullData;
}
hooks[fullRelatedHookName] = useFullRelatedHook;
});
typeKeys.forEach((typeName) => {
const listenerHookName = `use${toPascalCase(
String(typeName)
)}Listener`;
function useListenerHook(callback) {
const { repository } = useRpc();
const callbackRef = React.useRef(callback);
const listenerIdRef = React.useRef(null);
const isSubscribedRef = React.useRef(false);
callbackRef.current = callback;
const filteredCallback = React.useCallback(
(events) => {
const filteredEvents = events.filter(
(event) => event.type === typeName
);
if (filteredEvents.length > 0) {
const event = filteredEvents[0];
console.log(
`[${String(
typeName
)}Listener] Calling callback with:`,
event
);
const storageType = repository.getStorageType?.(String(typeName));
if (storageType === "singleton" && Array.isArray(event.payload) && event.payload.length > 0) {
const singletonEvent = {
...event,
payload: event.payload[0]
};
callbackRef.current(singletonEvent);
} else {
callbackRef.current(event);
}
}
},
[]
);
React.useEffect(() => {
if (listenerIdRef.current && typeof repository.offDataChanged === "function") {
repository.offDataChanged(listenerIdRef.current);
listenerIdRef.current = null;
isSubscribedRef.current = false;
}
if (!isSubscribedRef.current) {
listenerIdRef.current = repository.onDataChanged(
filteredCallback,
{
types: [typeName]
}
);
isSubscribedRef.current = true;
}
return () => {
if (listenerIdRef.current && typeof repository.offDataChanged === "function") {
repository.offDataChanged(
listenerIdRef.current
);
listenerIdRef.current = null;
isSubscribedRef.current = false;
}
};
}, [repository, filteredCallback]);
return () => {
};
}
hooks[listenerHookName] = useListenerHook;
});
function useDataListener(callback, options) {
const { repository } = useRpc();
const callbackRef = React.useRef(callback);
const listenerIdRef = React.useRef(null);
callbackRef.current = callback;
React.useEffect(() => {
if (listenerIdRef.current && typeof repository.offDataChanged === "function") {
repository.offDataChanged(listenerIdRef.current);
listenerIdRef.current = null;
}
listenerIdRef.current = repository.onDataChanged(
callbackRef.current,
{
types: options?.types || typeKeys
}
);
return () => {
if (listenerIdRef.current && typeof repository.offDataChanged === "function") {
repository.offDataChanged(listenerIdRef.current);
listenerIdRef.current = null;
}
};
}, [repository, options?.types]);
return () => {
};
}
hooks.useDataListener = useDataListener;
typeKeys.forEach((typeName) => {
const relatedHookName = `use${toPascalCase(
String(typeName)
)}Related`;
function useRelatedHook(id, targetType) {
const { repository } = useRpc();
const sourceData = reactRedux.useSelector(
(state) => state.rpc[typeName] || {}
);
const targetData = reactRedux.useSelector(
(state) => state.rpc[targetType] || {}
);
const [relatedData, setRelatedData] = React.useState([]);
const sourceDataString = React.useMemo(
() => JSON.stringify(sourceData),
[sourceData]
);
const targetDataString = React.useMemo(
() => JSON.stringify(targetData),
[targetData]
);
const getRelatedData = React.useCallback(() => {
try {
const result = repository.getRelated(
typeName,
id,
targetType
);
setRelatedData(result);
} catch {
setRelatedData([]);
}
}, [repository, id, targetType]);
React.useEffect(() => {
console.log(
`[${String(
typeName
)}Related] Fetching related data for id:`,
id,
"targetType:",
targetType
);
getRelatedData();
}, [
getRelatedData,
id,
sourceDataString,
targetDataString,
targetType
]);
return relatedData;
}
hooks[relatedHookName] = useRelatedHook;
});
function useHandleMessages() {
const { repository } = useRpc();
const handleMessages = (messages, callbacks) => {
repository.handleMessages(messages, callbacks);
};
const handleMessagesTyped = (messages, callbacks) => {
repository.handleMessages(messages, callbacks);
};
return { handleMessages, handleMessagesTyped };
}
hooks.useHandleMessages = useHandleMessages;
return hooks;
};
Object.defineProperty(exports, "Rpc", {
enumerable: true,
get: () => rpcTs.Rpc
});
Object.defineProperty(exports, "RpcRepository", {
enumerable: true,
get: () => rpcTs.RpcRepository
});
exports.RpcProvider = RpcProvider;
exports.createRpcHooks = createRpcHooks;
exports.extendStore = extendStore;
exports.useRpc = useRpc;
//# sourceMappingURL=index.js.map