Consider the following Redux code,
let cacheState = {};
const mapStateToProps = (state, ownProps) => {
const zug = cacheState.bar === state.bar ? "qux" : "doo";
return {
foo: state.bar,
zug
};
cacheState = state;
};
This code example spurs the following observations:
1) This seems to be the only way to cache state in a Redux component
2) This will break if multiple Redux components are created from this template
3) This is an anti pattern
What is the correct way for a component to be aware of previous states?
You likely want to use middleware to accomplish this. When your store is created, you'd do the first comparison (say if you were using localStorage) and store the cached state. You would then assume both your store and cache are synced. Every action that follows would go through the middleware and sync the state back with the cache.
You're right that what you're doing here is an anti-pattern which is why the middleware approach is useful in avoiding it. Dan Abramov's second set of Redux videos goes over this exact pattern if you'd like to check em out by searching the Building React Applications with Idiomatic Redux in Egghead, or here.
Related
When fetching state from an API with Zustand in a useEffect function what is the best practice for doing that? Right now I am using it very simply:
export interface ModeState{
modes: Mode[];
fetchModes: () => void;
}
export const useModeStore = create<ModeState>((set) => ({
modes: [],
fetchModes: async () => {
const modes: AcquisitionMode[] = await API.get(`/acquisition-modes`);
await set({ modes });
},
}));
In component render function:
const modeStore = useModeStore()
const modes = modeStore.modes
useEffect(() => {
modeStore.fetchModes()
}, [])
However the documentation seems to imply there are multiple ways this could be written to be more efficient in terms of performance, especially if my store grows more complex with more values and fetch functions. Is it best practice to make one store per API call? Use slices to get just the part of the store you need in each component? Should I be using the store differently in useEffect? I can't find a clear example online of how you should use the store in useEffect. The subscribe documentation does not seem to apply to the use case where you are using the store to fetch values with an async function.
I have used zustand in a similar fashion in the past. I would often have a sync method on the store which I call in a useEffect and pass to it any state that is available to the component and not the store. Another possibility could be to let a library optimized for fetching get the data and make it available to the store once fetched.
What the documentation refers to with regard to performance is that you can indeed select parts of your store with a provided selector. In these cases a rerender will only happen when
the previous and current selected value are different or
a custom provided equality function states that previous and current values are different.
If you want to get into more detail with regard to performance I can recommend this this article here (disclaimer, I wrote it)
Even so, those performance considerations do not influence so much how you would trigger a fetch from, say, a useEffect hook.
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
I was creating a very simple first Native app in React but my app is responding very slow to events such as touch
As suggested I have implemented several things like implementing FlatList instead of mapping and so on..
The two things which i think i might be doing wrong is updating state in
componentDidUpdate() {
var updateCoinData = [...this.props.cryptoLoaded];
socket.on('trades', (tradeMsg) => {
for (let i=0; i<updateCoinData.length; i++) {
if (updateCoinData[i]["short"] == tradeMsg.coin ) {
//Search for changed Crypto Value
updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
updateCoinData[i]['mktcap'] = tradeMsg['message']['msg']["mktcap"]
updateCoinData[i]['price'] = tradeMsg['message']['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(updateCoinData);
}
}
})
or maybe calling setState too often in my child component.
If anyone wants reference this is the link to my repository
https://github.com/irohitb/Crypto
These are the two files where most of the operations are happening
Child Repository
Parent Repository
I know it is probably too much to ask but can someone help me/suggest me how I can improve its performance.
There is a 'structural error', redux abusing. Not directly related to question (peformance) however it can have some indirect relation.
You're processing state/data locally while it's a reducers responsibility.
You should prepare payload from socket data and dispatch redux action/message. Reducer should create copy of prev state (use slice() to copy array, spread operator '...' is slow), search for matching id, update it and return as new state.
Main idea of reducer is extracting logic of state transitions to be simply testable. What can you test when only storing/receiving ready data set?
I'm new to React and Redux and I'm trying to write a simple application where a person can submit a URL for an image and it will show up on the page. Note that there is no backend to the application as of yet.
export const addImage = (url) => {
return {
type: ADD_IMAGE,
key: Guid.create().toString(),
payload: url
}
}
Adding an image creates an action of type ADD_IMAGE and my reducer updates the state consequently. However I also check if the URL is already in the list.
switch (action.type) {
case ADD_IMAGE:
if (state.find(image => image.url === action.payload)) {
return state;
} else {
return(
[
...state,
{key: action.key, url: action.payload}
]
);
}
break;
default:
}
The problem is that when I deny a post because the URL is already in the state I also want to convey that message to the user by showing it in a div next to the form. From what I've read I think I'm not supposed to try to access React state from reducers (if that is even possible) and... well.. I'm just stuck. I've been trying to find a simple guide on how to do this but I find nothing I can quite understand. After adding a database I guess I will have to do this as part of the async process but as I have it now I guess there should be some kind of simple solution.
You are starting to introduce logic into your reducer and this will inevitably lead to situation where you need to process some state outside of the reducer's scope.
The solution is to transfer your reducer logic into a thunk using a middleware package such redux-thunk (or similar package). This allows you to treat special kinds of actions as functions which means you can extend a plain action with specific action-related logic. The example you give of needing to dispatch an error action under certain conditions is an excellent use-case for redux-thunk.
Below is a example of how you might pull the logic out of your reducer into a thunk. You should note that, unlike reducers, thunks explicitly support fetching state and dispatching subsequent actions via the getState and dispatch functions.
Thunk example
export const addImage = (url) => {
return (dispatch, getState) => {
const key = Guid.create().toString()
dispatch({
type: ADD_IMAGE,
key,
payload: url
})
const state = getState()
// you would want to use a `selector` here to locate the existing image
// within the state tree
const exists = selectors.images.exists(state, url)
if (exists) {
dispatch(actions.ERROR_IMAGE_EXISTS({key, url}))
}
}
}
A note on selectors
You will see that I am using a selector to determine if the image exists. In the same way that thunks are the place to put your dispatch logic, a selector is the place to put your state-traversal logic. They are used to return portions of the state-tree or provide simple state-utilities such as the exists function shown above. Packages are available to help, for example reselect.
Follow on questions from comments
Are selectors not a built-in thing in Redux?
No they are not. Selectors are an idea that builds on top of redux and the concept exists as a place to put your state searching, caching, reading logic. This extracts the sometimes complex state traversal logic out of your thunks and components and into a nice tidy, structured collection of selectors.
Why use a selector instead of state.images.find(i => i.url === url)?
If you use a selector package then you get far more benefit than just a good separation of concerns, you get a big performance improvement (see usage example below).
Here are the headlines from the popular reselect package:
Selectors can compute derived data, allowing Redux to store the minimal possible state.
Selectors are efficient. A selector is not recomputed unless one of its arguments change.
Selectors are composable. They can be used as input to other selectors.
Why doesn't actions.ERROR_IMAGE_EXISTS(url) work for me
Because I just made that up for the example. The point is that you can dispatch actions from within the thunk, how you declare or get access to the action is up to you. I tend to centralise all my shared actions into an actions object that I import.
Selector usage example
Here is an example from my real-life code that shows how I use selectors to passing portions of the state as props to a react component:
const mapStateToProps = (state) => ({
model: services.model.selector.getSelected(state),
build: services.build.selector.getLastBuild(state),
recommendations: services.recommend.selector.getRecommendations(state)
})
Each of these selectors is finding the correct portion of the state tree and delivering it back ready for use. Nice and tidy, and if you use reselector, very efficient.
I'd like my React based SPA to render on server side (who's not these days). Therefore I want to combine React with react-router, redux and some build layer like isomorphic starterkit.
There is hapi universal redux which joins all together, but I am struggling with how to organize my flow. My data is coming from multiple endpoints of a REST API. Different components have different data needs and should load data just in time on the client. On the server instead, all data for a specific route (set of components) has to be fetched, and the necessary components rendered to strings.
In my first approach I used redux's middleware to create async actions, which load the data, return a promise, and trigger a SOME_DATA_ARRIVED action when the promise resolves. Reducers then update my store, components re-render, all good. In principle, this works. But then I realized, that the flow becomes awkward, in the moment routing comes into play.
Some component that lists a number of data records has multiple links to filter the records. Every filtered data set should be available via it's own URL like /filter-by/:filter. So I use different <Link to={...}> components to change the URL on click and trigger the router. The router should update the store then according to the state represented by the current URL, which in turn causes a re-render of the relevant component.
That is not easy to achive. I tried componentWillUpdate first to trigger an action, which asynchronously loaded my data, populated the store and caused another re-render cycle for my component. But this does not work on the server, since only 3 lifecycle methods are supported.
So I am looking for the right way to organize this. User interactions with the app that change the apps state from the users perspective should update the URL. IMO this should make the router somehow load the necessary data, update the store, and start the reconciliation process.
So interaction -> URL change -> data fetching -> store update -> re-render.
This approach should work on the server also, since from the requested URL one should be able to determine the data to be loaded, generate initial state and pass that state into the store generation of redux. But I do not find a way to properly do that. So for me the following questions arise:
Is my approach wrong because there is something I do not understand / know yet?
Is it right to keep data loaded from REST API's in redux's store?
Is'nt it a bit awkward to have components which keep state in the redux store and others managing their state by themselfs?
Is the idea to have interaction -> URL change -> data fetching -> store update -> re-render simply wrong?
I am open for every kind of suggestion.
I did set up exactly the same thing today. What we already had, was a react-router and redux. We modularized some modules to inject things into them – and viola – it works. I used https://github.com/erikras/react-redux-universal-hot-example as a reference.
The parts:
1. router.js
We return a function (location, history, store) to set up the router using promises. routes is the route definition for the react-router containing all your components.
module.exports = function (location, history, store) {
return new Bluebird((resolve, reject) => {
Router.run(routes, location, (Handler, state) => {
const HandlerConnected = connect(_.identity)(Handler);
const component = (
<Provider store={store}>
{() => <HandlerConnected />}
</Provider>
);
resolve(component);
}).catch(console.error.bind(console));
});
};
2. store.js
You just pass the initial state to createStore(reducer, initialState). You just do this on the server and on the client. For the client you should make the state available via a script tag (ie. window.__initialstate__).
See http://rackt.github.io/redux/docs/recipes/ServerRendering.html for more information.
3. rendering on the server
Get your data, set up the initial state with that data (...data). createRouter = router.js from above. res.render is express rendering a jade template with the following
script.
window.csvistate.__initialstate__=!{initialState ? JSON.stringify(initialState) : 'null'};
...
#react-start
!= html
var initialState = { ...data };
var store = createStore(reducer, initialState);
createRouter(req.url, null, store).then(function (component) {
var html = React.renderToString(component);
res.render('community/neighbourhood', { html: html, initialState: initialState });
});
4. adapting the client
Your client can then do basically the same thing. location could be HistoryLocation from React-Router
const initialState = window.csvistate.__initialstate__;
const store = require('./store')(initialState);
router(location, null, store).then(component => {
React.render(component, document.getElementsByClassName('jsx-community-bulletinboard')[0]);
});
To answer your questions:
Your approach seems right. We do the same. One could even include the url as part of the state.
All state inside of the redux store is a good thing. This way you have one single source of truth.
We are still working out what should go where right now. Currently we request the data on componentDidMount on the server it should already be there.