React redux sharing whole pages dependency injection - reactjs

My team develops a few different React applications that share similar components and pages.
how would approach sharing whole pages within different React applications? given that these pages have large amount of components in different hierarchies and each application has its own implementation of accessing the backend?
we are using React, React hooks, Redux, Saga.

In the end those pages are React components, so best way would be to identify all common components in the different React applications your team is developing, and create a component library to put all those components so the are all in a single code base, and is easier to maintain and use. I recommend you check this package https://www.npmjs.com/package/create-react-library
I have used before, is really easy to get it run. Still you can create a new library on your own, but this library will save you some time.
Another approach would be a step forward using micro-frontends architecture, I haven't used yet but looks really promising.
You can check this link https://micro-frontends.org/ and are many resources out there to learn about it. Like I said, I haven't used yet so I can't make you any recommendations about it.
If you decide to use a library for common components, then you can pass actions and hooks you need to execute in the library as props to the components in the library
export const RegisterView = ({ login, useMutation, useQuery, ...props }) => {
const [username, setUsername] = useState('')
const [email, setEmail] = useState('')
const [phone, setPhone] = useState('')
const [submitError, setSubmitError] = useState(null)
const [register, { loading }] = useMutation(REGISTER)
const { data: duplicatedUsername } = useQuery(CHECK_IF_USERNAME_EXIST, {
skip: !username,
variables: { username }
})
const { data: duplicatedEmail } = useQuery(CHECK_IF_EMAIL_EXIST, {
skip: !email,
variables: { email }
})
const { data: duplicatedPhone } = useQuery(CHECK_IF_PHONE_EXIST, {
skip: !phone,
variables: { phone }
})
the component is too large, this is only a fragment, but hope this shows the case you will probably need to use. In this case useQuery, useMutation are hooks from apollo-client I'm passing from the app, the schemas for this calls are also in the library. The login prop is also a hook that calls the right login request, so the RegisterView component only receive those props coming from the app where is being imported and executes them.
Whatever logic your common component need to use or execute that is not shared between all apps, you must put it outside the library and pass it as props.
The create-react-library package also comes with a react app inside to test the components in your library so you are able to debug them and test them in isolation on the apps where you will import them.

Related

Is there a downside to using a custom hook as a store instead of tools like redux or useReducer?

Just like the title, I am wondering if I could use a simple hook to save data which can be used by any consumer
export default () => ({data: 'some random data'})
And any consumer can use it like so
const { data } = useSomeDataStore()
If we want to make the data updatable, I can add a setter to it like so:
export default () => {
const [data, setData] = useState('some random data')
return {
data,
setData
}
}
So I am wondering, why do we need ContextAPI, redux and a thousand other state management tools? I understand that for complex and data intensive app, we might need some of these tools to manage it, but for a very simple app design, what are the downsides of this implementation?

How to use StoreApi in zustand

From the docs of zustand I found that together with set and get parameters they provide an additional parameter at StateCreator, called api.
Example bellow
import create, { StateCreator } from 'zustand'
import type { Store } from './store.types'
const globalStateCreator: StateCreator<Store> = (set, get, api) => ({
...mySlice(set, get, api),
})
What does it stands for? What are the best practices for using it?
If you're using React, you're probably using hooks to do something like this:
const color: PaletteColor = usePaintbrush((s) => s.color);
The hooks make zustand seem more self-contained than it really is. You have a lot of control over the state.
Hooks are limited to components, but you can use the api methods bound to the store from anywhere, imperatively.
// .ts
const color: PaletteColor = usePaintbrush.getState().color[shade];
// .tsx
const color = useRef<PaletteColor>(usePaintbrush.getState().color[shade]);
useEffect(() => usePaintbrush.subscribe((s) => (color.current = s.color[shade])), [shade])
Store actions are not required to be in the store either!
// at module level
const setColor(color: PaletteColor) => usePaintbrush.setState({ color })
You're unlikely to touch the api parameter unless you're creating a middleware.
Docs cover specific example usage with the persist middleware
The persist api enables you to do numbers of interactions with the persist middleware from inside or outside a React component.
references (updated #1033):
store without actions
persist middleware api
middleware that changes store type

Saving and restoring the full state of a React app

I'm building a VSCode extension using React. When the tab loses focus, VSCode shuts down the WebView and only reloads it when the tab gets focus again. The app fully reloads from the start.
VSCode already provides a way to save arbitrary state object and then get it back when restoring the WebView.
What remains is to serialize the full state of the React app (the whole React DOM, states etc) into a simple JSON-like object.
How can I serialize the full state of the React app and then reload it?
I know that React has some features like Server-Side Rendering - maybe they can be used to serialize DOM and state?
To accomplish that, you need some kind of global state object, which holds all the state data that you want to preserve. You can then save and restore this object using the VSCode API you mentioned.
There are several ways to do that and different 3rd-party libraries for this purpose. Here I will outline some of the options.
Context API
Context API is built into React. You need to create an instance and wrap your app with a context provider. Then you can access the state in your child components with useContext.
Here's an example of how you would use it to store some user and page data, as well as control some textarea field in a child component, which would normally be a local state.
const App = () => {
const [user, setUser] = useState();
const [textAreaValue, setTextAreaValue] = useState("");
const [currentPage, setCurrentPage] = useState("home");
// etc.
// this is your global state object that you can then save using VSCode magic
const globalState = { user, setUser, /* etc. */ };
return (
<GlobalStateContext.Provider value={globalState}>
<Child />
</GlobalStateContext.Provider>
);
}
...
const Child = () => {
const { textAreaValue, setTextAreaValue } = useContext(GlobalStateContext);
const handleChange = (e) => {
setTextAreaValue(e.target.value);
}
return (
<textarea value={textAreaValue} onChange={handleChange} />
);
}
Of course, this will be cumbersome if you have a lot of state data to manage. Furthermore, whenever any field in the context changes, all components using it will re-render. This could cause performance issues, so this solution does not scale well. It should be fine for a simple application though.
Custom store hook
Another solution would be to use a global store functionality. You could write a custom hook for that and then use it like this:
const Child = () => {
const { textAreaValue, setTextAreaValue } = useStore("textarea");
const handleChange = (e) => {
setTextAreaValue(e.target.value);
}
return (
<textarea value={textAreaValue} onChange={handleChange} />
);
}
I won't provide a full example of how to implement this for brevity, but here is one guide that could be useful.
3rd-party library
There are also 3rd-party libraries that implement the global store functionality. A popular choice is Redux, although I personally wouldn't recommend it if you haven't used it before, due to its verbosity and somewhat of a learning curve. Other options include Recoil, react-hooks-global-state and ReactN.

Use external data in XState FSM

I'm trying to shim XState into an existing state management system (in a React app) and I'm trying to figure out how to represent the state that is already captured in the legacy state management without duplication.
import {useLegacyState} from 'legacy-state-system'
import {useMachine} from '#xstate/react'
import {MyMachine} from '../machine'
const MyComponent = () => {
const [data, setData] = useLegacyState();
const [state, send] = useMachine(MyMachine)
.....JSX etc....
}
For some of the data there is no overlap, but in at least one case (selecting an item on screen, causes the app to send({type: "SELECT_ITEM", itemId: "xyz"}) and fire setData("XYZ")), both legacy and new systems care about the item. XState is being used for UI State Management but the legacy system has side effects that depends on its internal state, so I can't only have data in XState.
My understanding of XState is that I should represent itemId as continuous data in XState's context, but that duplicates the data and I'm concerned that presents a maintenance issue since all developers forever will need to know to update both simultaneously. Is there a way for XState Context to take a value from a runtime-evaluated function? I know that there's assign if I want to push values into Context but that's susceptible to the same maintenance issue so I'm looking for a way to pull values from legacy-state-manager when I call state.context.itemId.
What about wrapping useMachine and using that instead?
import { useMachine as useXStateMachine } from '#xstate/react'
export const useMachine = (machine, options) => {
const [data, setData] = useLegacyState();
const [state, send] = useXStateMachine(machine)
const context = new Proxy({}, {
get: (_, prop) => {
try {
return state.context[prop] || data[prop]
} catch (_) {
return data[prop]
}
}
})
return [{...state, context}, send]
}
The view or the react layer gets updated every time the data store changes and renders it. Typically in a MVC architecture, these logics are built into the controller, where multiple data stores are combined and the resulting data is returned to the UI. In a hook based approach like how you have used, you an create services, that wraps the datastore logics, within it and return only the data required at the UI level.
import {useCustomService} from './services';
const MyComponent = () => {
const [uiData, updateUI] = useCustomService();
}

How to have a separated js file that handles the API calls in Reactjs

I want to structure my react project and set my APIs and methods that makes the calls in a separate file. I am coming from Angular background, in Angular there are services which handle all http calls.
So, assume that i have a functional component called Supplier that have two files, Supplier.js and Supplier.css. I want to add a new file and call it SupplierServices.js in this file i want to write all methods that makes the calls to the server and simply import it in Supplier.js.
how to achieve this?
Your file with request.js:
import axios from 'axios';
export default () => axios.get('/test');
Your component component.js:
import React from 'react';
import {request} from './request';
const Component = () => {
.....
request();
}
Components are just functions. You don't necesarily need to render anything in them, they can hold logic or API calls if you want. Before hooks, and when working without a state manager like Redux, you usually saw either HOCs (higher order components) or render props be used to compose logic together, here are some examples:
Using HOCs
Using hooks and render props
You can use hooks alone as well
There's also nothing stopping you from 'emulating' services like in your example, it's mostly a matter of taste and being aware of the pros and cons of each method.
You can do it like this
1. Create the method you want to export inside suppliers devices.js.
SupplierServices.js
Export const fetchData = async () => {
Const res = await axios.get(your url)
Console.log(res)
}
2. Now import the fetchData method from supplierServices
Import { fetchData } from './SupplierServices'
Const Supplier = () => {
useEffect(.() =>{
fetchData ()
} )
}
i used to use flux architecture - this pattern can be use in every framework.
More info about implementing redux
└─ client
└─ redux
└─ services
└─ actions
└─ reducers
Later encapsulating logic parts into individual parts (user.service, event.service, transaction service) etc.
Usually, event calls actions, which call service (in service promisses are operated asynchronously - try-catch), which return data(success) or error - that call the reducer and change the application's UI state (a bit complexity - but the advantage is the possibility of management all components - regardless of their relationality (global status).

Resources