What is the right place to add a preprocessor in Redux - javascript

I have added redux to my application. I want to understand what is the right place to add preprocessor, that would processor data returned by asynchronous call made by reducer.
I have a fetch function that is generic and returns a resource based on what param is passed to it.
const mapDispatchToProps = dispatch => ({
fetchUsers: filters => dispatch(fetch('USERS')),
fetchRegions: filters => dispatch(fetch('REGIONS'))
});
Now once this call is done, it would update the props that have following binding:
const mapStateToProps = store => ({
users: store.users
regions: store.regions
});
Now before the above mentioned update on props happen, I would like to preprocess the array based on how the component that would use these want the array to be.
Where should I write the preprocessor.

The most sensible place would probably be within the componentWillReceiveProps lifecycle method. In here you can check if that data you are expecting to receive has changed, and if so you can do your relevant processing on it and store the result in local state.

Related

How can I fetch backend data in child component and then retrieve an object from store?

My parent component <Room/> build children components <RoomSensor/>, when parent build these children I also send to the <RoomSensor/> uuid, by this uuid I fetch sensor data from a backend.
Store is array of objects.
// Parent <Room/>
return props.sensors.map((uuid, index) => {
return <RoomSensor key={index} uuid={uuid}/>
})
// Children <RoomSensor/>
const RoomSensor = props => {
useEffect(() => {
console.log("useEffect")
props.fetchSensor(props.uuid)
}, [props.uuid])
console.log(props.sensor)
return (
<div className={"col-auto"}>
<small><b>{props.sensor.value}</b></small>
</div>
)
}
let mapStateToProps = (state, props) => {
return {
sensor: filterSensor(state, props.uuid)
}
}
let mapDispatchToProps = {
fetchSensor,
}
export default connect(mapStateToProps, mapDispatchToProps)(RoomSensor)
// Selectors
export const getSensor = (state, uuid) => {
return _.filter(state.sensors, ["uuid", uuid])[0]
}
export const filterSensor = createSelector(
getSensor,
(sensor) => sensor
)
And I cannot understand two things:
When I do refresh I get.
TypeError: Cannot read property 'uuid' of undefined
I understand that there is no data in the state yet, that's why such an error occurs. Is it possible not to render the component until the data comes from the server?
If I comment <small><b>{props.sensor.value}</b></small> no errors occur, data appears in the store, then I uncomment this line and voila everything works. But in the console I see too many component rerende. What am I doing wrong? Is there something wrong with the selector?
In general, I want each sensor component to render independently of the others.
The following is based on a few assumptions derived from the shared code and output:
Currently, there's a hard-coded list of 4 sensor UUIDs.
createSelector is from the reselect package.
_ references an import of the lodash package.
"Is it possible not to render the component until the data comes from the server?"
The short answer to this is yes. There're several approaches to achieve this, so you'll want to evaluate what fits best with the structure of the app. Here're 2 approaches for consideration:
Retrieve the list of sensors from the server. Initialize the store with an empty list and populate it upon getting data back from the server.
In getSensor, return a default value if the uuid isn't in the list.
Either way, I'd recommend adding default state to the store. This will help reduce the amount of code required to handle edge cases.
Here's a rough example of what the new reducer and selector for (1) might look like:
export const storeReducer = (state, action) => {
let nextState = state;
if (!state) {
// State is uninitialized, so give it a default value
nextState = {
sensors: [],
};
}
switch (action.type) {
case 'RECEIVE_SENSORS':
// We received sensor data, so update the value in the store
nextState = {
...nextState,
sensors: action.sensors,
};
break;
default:
break;
}
return nextState;
};
export const getSensors(state) {
return state.sensors;
}
The action upon receiving the data from the server, could look something like:
dispatch({
sensors,
type: 'RECEIVE_SENSORS',
})
"...in the console I see too many component rerende[rs]"
Without additional context, it's hard to say exactly why the re-renders are happening, but the most likely cause is that each call to props.fetchSensor(props.uuid) changes the data in the store (e.g. if it's overwriting the data).
From the console output you shared, we see that there're 16 re-renders, which would happen because:
Each of the 4 instances of RoomSensor calls fetchSensor
This results in 4 updates to the store's state
Each update to the store's state causes React to evaluate each instance of RoomSensor for re-render
Hence, 4 state updates x 4 components evaluated = 16 re-renders
React is pretty efficient and if your component returns the same value as the previous run, it knows not to update the DOM. So, the performance impact probably isn't actually that significant.
That said, if the above theory is correct and you want to reduce the number of re-renders, one way to do it would be to check whether the data you get back from the server is the same as what's already in the store and, if so, skip updating the store.
For example, fetchSensor might be updated with something like:
const existingData = getSensor(getState(), uuid);
const newData = fetch(...);
// Only update the store if there's new data or there's a change
if (!existingData || !_.isEqual(newData, existingData)) {
dispatch(...);
}
This would require updating getSensor to return a falsey value (e.g. null) if the uuid isn't found in the list of sensors.
One additional tip
In Room, RoomSensor is rendered with its key based on the item's index in the array. Since uuid should be unique, you can use that as the key instead (i.e. <RoomSensor key={uuid} uuid={uuid} />). This would allow React to base updates to RoomSensor on just the uuid instead of also considering the order of the list.

React Hooks with Redux persisting the App state

I just started learning react hooks and react-redux hooks and as far I understood everything. But one thing is keep drilling my brain, so I would like to ask more experienced developers here.
If I have more robust app, where I intend to have Redux taking care of whole state and wanna use React hooks fro side effects, do I really need React Hooks?
I have separate functional layer (containers => where all the decisions are being made with redux) and displaying layer (components => where components are dumb and obtain just data they are suppose to render)
Whats bugging me is I make a API call in initial page loading and I would like to use useEffect hook, but im not conviced I should do that when I can useSelector from redux and useDispatch.
here is the code I would like to update into hook style:
const mapStateToProps = (state) => {
return {
cities: state.weather.cities,
}
}
const mapDispatchToProps = (dispatch) => {
const fetchForUpdate = (cities) => {
return cities.map((city) => {
return dispatch({ type: FETCH_START, payload: city.name })
})
}
return {
fetchForUpdate: fetchForUpdate,
}
}
const WeatherListContainer = (props) => {
const { cities } = props
const cityData = cities.map((oneCity) => {
return (
<WeatherItemContainer
name={oneCity.name}
data={oneCity.data}
key={oneCity.name}
/>
)
})
return <WeatherList item={cityData} />
}
const enhance: Function = compose(
connect(mapStateToProps, mapDispatchToProps),
lifecycle({
componentDidMount() {
console.log(this.props.cities, 'this.props.cities')
this.props.fetchForUpdate(this.props.cities)
},
}),
)
export default enhance(WeatherListContainer)
how can I fetch with redux hooks or react hooks? Or can I combine it? like use useEffect and then save it from local store to global store? Isnt it a bit ineffective?
Redux requires a middleware such as redux-thunk to dispatch asynchronous actions (an API call). If you plan on calling an API multiple times throughout your app, it makes sense to use redux-thunk and dispatch an asynchronous action, though this dispatch might still need to occur within useEffect/componentDidMount. If you only plan on a single API call, or if a specific side effect is unique to one component, there is no need to implement the middleware. For a single API call, you can send your request within useEffect/componentDidMount and then dispatch the result with a synchronous action to the redux store, without having to ever store it in component state.
https://redux.js.org/advanced/async-actions
I think there are some confusions. React hooks are used for sideEffects where redux hooks are for using the store more efficientl. Lets consider a scenario like bellow.
You are fetching a todo list from API and wanna use it all over the app. You have multiple components and you are gonna need the todo list in every component. In that case, you will call the api either using a middleware like redux-thunk or by other means like caling it in a useEffect( which is not a good practice) and save it to redux store using redux hooks. And whenever you redux store is updated, you will need to show the data in components. How will we do that? we will use react hooks to apply the sideEffects.
Here we will get the data from redux store using redux hooks. Than in a reactHooks like useEffect we will update a state of the component using useState. So here you can see, both reactHooks and reduxHooks are completely different in terms of functionality. one is storing and serving data which is reduxHooks and another one is showing the data when ever its added or updated which is reactHooks.
Hope you will find it understandable.

when I map my redux state with component state I am getting an error

when you click advanced sports search button I need to display drawer with my api values.
but right now when I map my redux state with component state I am getting an error.
Actions must be plain objects. Use custom middleware for async actions.
can you tell me how to map my state.
so that in future I can fix all my redux issues by myself.
providing code snippet and sandbox below.
all my map state is done in tab-demo.js
https://codesandbox.io/s/rlpv50q8qo
getSportsPlayerHistory = values => {
this.props.fetchHistorySportsDatafromURL();
};
toggleDrawer = (side, open) => () => {
if (open === true) {
this.getSportsPlayerHistory();
}
this.setState({
[side]: open
});
};
const mapDispatchToProps = dispatch => {
return {
onDeleteAllSPORTS: () => {
// console.log("called");
dispatch(deleteAllPosts());
},
addFavoriteSPORTSs: data => {
dispatch(addFavoriteSPORTSs(data));
},
fetchHistorySportsDatafromURL: () => {
dispatch(fetchHistorySportsDatafromURL());
}
};
};
Actions need to return plain objects, your fetchHistorySportsDatafromURL action returns a function. If you make your history reducer function async then you can make an async function to make your API call there and return the result to state.
API call in reducer
This works, but isn't ideal as you want your reducers to be pure functions, as-in, no side-effects, same input always produces the same output
You can also make the API request in the component's callback handler asynchronously and pass the result to the dispatched action.
API call in component then dispatched in action
This is a good solution and works great for small projects, but couples network business logic into your UI display components, which also isn't as ideal since it reduces code re-usability.
If you still want to keep your API logic separate from your component (which is a good thing), redux-thunk is a way to create asynchronous action creators, which is very similar to the pattern of your original code.
API call in action using redux-thunk
This is the most ideal as it completely de-couples business logic from your UI, meaning you can change back-end requests without touching front-end UI, and other components can now also use the same action. Good DRY principal.
Not really sure what you wanted to do with the new state, but this should get you to a good spot to handle that in your mapStateToProps function.

updating a prop when another prop is received in mapStateToProps

I am attempting to implement a basic filter in my React app. I have two props that are managed by Redux. They look something like the following:
const mapStateToProps = state => {
return {
groceries: getSortedGrocers(state.groceries),
searchFilter: state.searchFilter
};
};
The problem I'm facing is when searchFilter is updated, I need to filter the groceries array, which is ultimately this.props.groceries in my Component instance.
What is the best design pattern for this? Should I be using a lifecycle event so whenever searchFilter is updated, I filter groceries? Can I get pointed to the correct documentation for how to handle filtering properly so if a searchFilter exists, I narrow down what groceries to display? Ultimately, somewhere, something such as this will exist:
if ( this.props.searchFilter ) { groceries.filter( ... ) } I'm just not entirely sure where to handle the filtering logic.
Your filtering should be already done in Redux, in the getSortedGrocers function.
const mapStateToProps = state => {
return {
groceries: getSortedGrocers(state.groceries, state.searchFilter)
};
};
And in the Redux getter:
export const getSortedGrocers = (groceries, filter) => {
// do sorting here
};
That's the standard design pattern for filtering stuff, as far as I'm aware. You can also do the filtering on your render method, but that's not encouraged (though not any less efficient, funnily enough).
Whenever searchFilter is updated mapStateTopProps will be called. You can do the filtering in there. If you need to use existing props from the component you get them as a second param.
mapStateToProps = (state, existingProps) => {}
Maybe I'm misunderstanding the question. If some of the info you need is passed to the component as props you should use the second param.

Callbacks using redux-thunk / redux-observable with redux

I am learning how redux works but its a lot of code to do simple things. For example, I want to load some data from the server before displaying. For editing reasons, I can't simply just use incoming props but I have to copy props data into the local state.
As far as I've learned, I have to send a Fetch_request action. If successful, a fetch_success action will update the store with new item. Then updated item will cause my component's render function to update.
In component
componentWillMount() {
this.props.FETCH_REQUEST(this.props.match.params.id);
}
...
In actions
export function FETCH_REQUEST(id) {
api.get(...)
.then(d => FETCH_SUCCESS(d))
.catch(e => FETCH_FAILURE(e));
}
...
In reducer
export function FETCH_REDUCER(state = {}, action ={}) {
switch (action.type) {
case 'FETCH_SUCCESS':
return { ...state, [action.payload.id]: ...action.payload }
...
}
Back in component
this.props.FETCH_REDUCER
// extra code for state, getting desired item from...
Instead, can I call a react-thunk function and pass some callback functions? The react-thunk can update the store and callbacks can change the component's local state.
In component
componentWillMount() {
this.props.FETCH_REQUEST(this.props.match.params.id, this.cbSuccess, this.cbFailure);
}
cbSuccess(data) {
// do something
}
cbFailure(error) {
// do something
}
...
In action
export function FETCH_REQUEST(id, cbSuccess, cbFailure) {
api.get(...)
.then(d => {
cbSuccess(d);
FETCH_SUCCESS(d);
}).catch(e => {
cbFailure(d);
FETCH_FAILURE(e);
});
}
...
Is this improper? Can I do the same thing with redux-observable?
UPDATE 1
I moved nearly everything to the redux store, even for edits (ie replaced this.setState with this.props.setState). It eases state management. However, every time any input's onChange fires, a new state is popping up. Can someone confirm whether this is okay? I'm worried about the app's memory management due to redux saving a ref to each state.
First of all, you should call your API in componentDidMount instead of componentWillMount. More on this at : what is right way to do API call in react js?
When you use a redux store, your components subscribe to state changes using the mapStateToProps function and they change state using the actions added a props through the mapDispatchToProps function (assuming you are using these functions in your connect call).
So you already are subscribing to state changes using your props. Using a callback would be similar to having the callback tell you of a change which your component already knows about because of a change in its props. And the change in props would trigger a re-render of the component to show the new state.
UPDATE:
The case you refer to, of an input field firing an onChange event at the change of every character, can cause a lot of updates to the store. As mentioned in my comments, you can use an api like _.debounce to throttle the updates to the store to reduce the number of state changes in such cases. More on handling this at Perform debounce in React.js.
The issue of memory management is a real issue in real world applications when using Redux. The way to reduce the effect of repeated updates to the state is to
Normalize the shape of state : http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
Create memoized selectors using Reselect (https://github.com/reactjs/reselect)
Follow the advice provided in the articles regarding performance in Redux github pages (https://github.com/reactjs/redux/blob/master/docs/faq/Performance.md)
Also remember that although the whole state should be copied to prevent mutating, only the slice of state that changes needs to be updated. For example, if your state holds 10 objects and only one of them changes, you need to update the reference of the new object in the state, but the remaining 9 unchanged objects still point to the old references and the total number of objects in your memory is 11 and not 20 (excluding the encompassing state object.)

Categories

Resources