Is it possible to initialize React Final Form from a saga? - reactjs

I'm migrating from Redux Form to React Final Form on a project that's using React and redux-saga. A common pattern for initializing forms in the project looks like this:
import { initialize } from "redux-form";
import { call } from "redux-saga/effects";
// ...
const data = yield call(axios.get, "/backend/api/endpoint");
yield initialize("someFormName", data);
// ...
Is there a way to do something similar for Final Form?
Creating an object in the store, setting it from the saga and passing it to Final Form's initialValues prop works, but I suspect it's not the optimal solution, and I'd rather not create a store for every single form in the application.

If you can get your initial values all the way into the initialValues prop of <Form/>, then yes. How you connect() up your component to do so is up to you.

Related

Where to store instance of API object in react (next.js) app?

I'm trying to build react (with next.js) based chat client using the LiveChat CustomerSDK API.
I currently use a useEffect hook to initialize and then store the object with a useState hook.
const [customerSDK, setCustomerSDK] = useState();
useEffect(() => {
const sdk = CustomerSDK.init(config);
setCustomerSDK(sdk)
}, []);
But I am wondering whether useState is the best way for the customerSDK object, since it doesn't need to trigger a re-render. Therefore I also considered using useRef instead or maybe just storing it in the window object. But neither "feels right".
I have to consider that I need to access the customerSDK object to set the listeners (e.g. customerSDK.on('connected', ...) and also for example sending messages (customerSDK.sendEvent(...)
Is there a recommended/preferred way to approach a situation like this?
Theres another option if you know you only have 1 project accessing it, simply put it in a file and export it, then import this everywhere you need it. If it needs no context (i.e. react state) from your components, why have it stored in a component at all?
const sdk = CustomerSDK.init(config);
export default sdk
Then import that where you need it. It will act as a singleton.

Using only object id and lastUpdate for diff on useEffect in React

I have a large and deep object which I render using a React functional component (composed of child components). I also use Redux in the project which interacts with an API. To keep the app fast, I keep a local copy of the object in the component state and dispatch changes when occurred. The objects can also change in different parts of the app.
Each time the object changes in Redux its lastUpdated field is updated with the current time (epoch) by Redux. So instead of doing a deep (and expensive) diff on the whole object it is sufficient to compare the object id (in case the component has to display a different object) and the lastUpdated value (in case the object itself got changed).
This is what my functional component looks like:
interface Item {
id: string
lastUpdated: number
// more fields here
}
interface Props {
item : Item
}
export default function ItemPage(props: Props){
const [displayItem, setDisplayItem] = useState<Item>(props.item)
useEffect(() => {
if (props.item.id !== display.item.id && props.item.lastUpdated !== displayItem.lastUpdated){
setDisplayItem(props.item)
// ... some logic comes here
}
}, [props.item.id, props.item.lastUpdated])
return (
// ... render code here
)
}
This code cause the following error:
React Hook useEffect has missing dependencies: 'item.id' and
'item.lastUpdated'. Either include them or remove the dependency array
react-hooks/exhaustive-deps
I have disable the error with:
// eslint-disable-next-line react-hooks/exhaustive-deps
Questions:
Is it safe to to disable the error as I know the logic of my Redux (and use the more efficient diff)? Is there a better solution?
Is it safe to completely remove the useEffect array as I make the id/lastUpdated diff myself?
Note: I am not looking for the general "do not disable as it will come back to bite you ..." answer.
Looks like you need React.memo with second argument isEqual. Is equal works like shouldComponentUpdate. Read reactjs docs more about React.memo
UPDATE:
I've added codesandbox as example

Shouldn't useContext() only be used for low frequent updates? (mobx-react-lite)

Almost all examples (even the official documentation) use mobx-react-light in combination with useContext() hook.
However, React, many articles and blog-posts advice to NOT USE useContext() for middle/high frequent updates. Isn't state something which can update very frequenty?
Should one use the package in combination with the hook or will performance issues occur?
useContext() is only used to get your store value (reference) and this value is not updated frequently, usually you only set your stores once and don't touch it after. When you use actions you only change observable values of your store and not the store itself. So basically Context is only used to pass reference to the store down the tree, after that all the work performed by MobX only.
Example from MobX docs:
import {observer} from 'mobx-react-lite'
import {createContext, useContext} from "react"
const TimerContext = createContext<Timer>()
const TimerView = observer(() => {
// Grab the timer from the context.
const timer = useContext(TimerContext) // See the Timer definition above.
return (
<span>Seconds passed: {timer.secondsPassed}</span>
)
})
ReactDOM.render(
<TimerContext.Provider value={new Timer()}
<TimerView />
</TimerContext.Provider>,
document.body
)
So you pass value of new Timer() to TimerContext.Provider once when you render your app and it will never be changed or updated after that. Even docs says:
Note that we don't recommend ever replacing the value of a Provider with a different one. Using MobX, there should be no need for that, since the observable that is shared can be updated itself.
However, if you don't have SSR or don't test your app, then you don't even need to use Context at all, you can just use global variables/singletons as your stores and it will work perfectly fine.
Example:
// Initialize timer somewhere
export const myTimer = new Timer()
// You can use directly in the same file or import somewhere else
import { myTimer } from './file-with-your-timer'
// No props, `myTimer` is directly consumed from the closure or from another file
const TimerView = observer(() => <span>Seconds passed: {myTimer.secondsPassed}</span>)
ReactDOM.render(<TimerView />, document.body)
Quote from docs:
Using observables directly works very well, but since this typically introduces module state, this pattern might complicate unit testing. Instead, we recommend using React Context instead.
More about best practices with React: https://mobx.js.org/react-integration.html

Best practice for only getting parts of the store/state in React component using redux

I implemented by using redux thunk that if one component is mounted, that the store is filled by calling an API.
The data which are requested have much more fields than I need in particalur components. So could you please give me a tip what the best practice would be to get only parts of the data. Because storing a reduced version of the data also (kind of duplication) wouldn't be a good practice, would it?
If I understood the question correctly, you can use the selectors
You can deconstruct data.
For example, when you connect your component to the redux state.
const mapStateToProps = state => {
relevantPartOfTheState: state.some.part.of.the.state
}
This is essentially what selectors do.
The data which are requested have much more fields than I need in particalur components
You are not required to save all of it. You can have something like this.
switch (action.type) {
case RECEIVE_USELESS_DATA:
return action.payload.onlyRelevantData
}
You can either use selectors as mentioned above or somethine like that:
const mapStateToProps = state => {
relevantPartOfTheState: fetchSliceOfReducer(state)
}
The fetchSliceOfReducer(state) can be a function from inside the reducer file where you take only part of the state. Obviously you have to import the function in your component file and it can be reused for other components. So if your state is something like that
state = {
value: true,
name:"Michael"
}
your function would be const fetchSliceOfReducer = state => state.value
Selectors would be more appropriate for a growing app, where as the example above is nice and neat for something smaller

Redux - external libs and "React" in reducer?

I have simple Redux reducer, but for some actions I need to show notifications, in order to do so I need to trigger my custom notification function within the reducer so:
case REDUCER_ACTION_NAME:
notificationDisplay.success("Message", {
additionalStuff: extraOptions
});
What's even worse I'm using react-intl for translations and I need the "Message"o be translate-ready so I'm adding this to the mix:
case REDUCER_ACTION_NAME:
notificationDisplay.success(<FormattedMessage id="message" defaultMessage="Message" />, {
additionalStuff: extraOptions
});
It creates a translation in span and requires react so my reducer starts with these imports:
import React from 'react';
import notificationDisplay from 'my-notifications';
import { FormattedMessage } from 'react-intl';
import {
// all the actions
} from './actions.jsx';
// reducer
Is it okay? I feel something here is off - like importing React in reducers is an anti-pattern, because all reducer examples I could find are so clean and sleek and there are no external libs there whatsoever.
Am I right or am I wrong and my code is perfectly fine?
You should not do any kind of computations in reducer. It should change the state and nothing else. The way you are using it is a complete anti-pattern. Because it is doing some UI actions. And Redux is nothing to do with the UI. It should be used as the store and only the store.
But you can use Actions which is way better than doing it in reducer.
Best way to achieve your goal is to use your reducer to just push the messages into an array in the redux store. And create a Container that uses that messages array to show success or error messages. And create a timer that removes the message from the array after some time.
Just look at the https://github.com/diegoddox/react-redux-toastr repo they are doing it very well.
Thanks
Akhil P

Resources