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.
Related
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.
I am writing an angular application. in which I am managing state using redux.
I have a store like below
export interface State {
data {
items: any[];
}
}
I have a return selector for getting Items like below
export const getItems = createSelector(getItemState, fromItem.getItems);
fromItem.getItems is like below =>
export const getItems = (state: State): any[] => state.items;
and in my component I have subscribe for selector of items like below
this.store.select(getItems).subscribe((items) => {
this.localItems = items;
}
everything is working fine but in getItems subscription I am getting the reference to the items which are stored in the store. And, if I update any local item it also gets reflected into the store.
I was expecting the subscription of selector (getItems) to return a cloned copy of items from the store but it is returning the reference.
Am I doing anything wrong or is there any way to get a cloned copy of items from the store?
What you're describing is the correct behavior.
State mutations should only happen in reducers in a pure way.
Doing it this way makes using a store performant, we can simply check if the reference is the same - this is very cheap.
So i've been reading a lot lately on react state and props.
My app wasn't that big, but now i'm facing a problem that seems to be commun for a lot of people, and i'm trying to find the best way to implement this.
My app is simple. A SearchBar on top, that display a list of contact. My search bar is a component and is updating a react-redux store with the results of the searchBar value (calling a backend with axios). Till here everything works great.
When the results array is populate (in redux store), my container rerender the results array. Like this:
class Suggestions extends Component {
render() {
console.log('before map: ', this.props.contacts);
const {
contacts,
...rest
} = this.props;
const options = contacts.map((contact, index) => (
<Contact
key={contact.id}
renderToaster={renderToasterFunction}
contact={contact}
/>
));
return <div>{options}</div>;
}
}
const mapStateToProps = (state, props) => ({
contacts: state.contact.results,
});
export default connect(mapStateToProps)(Suggestions);
The problem happen in my Contact component, My list is a lirs of sometimes 10 contacts that are display on the same page. So my problem is that each Contact component need to have it's own state (to add or edit info exemple: if you need to add a new phone number).
//contact component
state = {
contactState: ???
}
...
render(){
//exemple for simplicity
return <div>{this.state.contactState.name}</div>
}
I've founded on react website that it's not a good idea to copy props from parent in state of child. And in my case i've seen it, because if i do this
...
state = {
contactState: this.props.contact <--info from parent
}
first search is ok, but second search with an other letter, results list is not updated and i still see some results of first search.
so i've tried to change my contact component to this:
//contact component
state = {
contactState: ???
}
...
render(){
//exemple for simplicity
return <input value={this.props.contact.name} onChange={this.handleChange}/>
}
And this is working great in term of visual update, all my contact are update even if i do 3-4 searches. But my problem is that, now when i want to edit the name i need to store all my contactState somewhere before saving this and second problem, because my component display {this.props.contact.name} when i edit this, the user can't see the new value, because i can't edit props.
So is there a way to render state from props in a child everytime the parent state change. Or is there a way to 1) save the state when the user edit a contact and 2) display the new value he has written ?
What is the best way when dealing with .map() to have one state foreach children that can be re-renderer when the parent state change and rendering all children with their new state.
Thank you for your help.
Don't hesitate if you need more precisions.
I'm not sure to understand everything but if I get what you want to do:
A simple solution could be to dispatch an action on the onChange
The reducer which catch the action will update your redux store
The props will change and the View too.
But that's will make you dispatch A LOT of actions...
Other option :
Use a state in every Contact-Component which duplicates props
state = {...this.props.contact}
Modify the state on the change handler and use it as value too.
Save and dispatch the "final name" to update redux store and call the api at the same moment to update in on your server
Let me know if that's clear enough
I have something like this:
ngOnInit() {
this.reduxServiceHandle = this.reduxService.subscribe(() =>
this.reduxUpdates()
);
}
reduxUpdates(): void {
const newState: TransportationControlTowerState = this.reduxService.getState();
// Check if feature groups object has changed
if (!isEqual(this.groups, newState.groups)) {
...
this.groups = newState.groups;
}
}
This is my reducer:
case TransportationControlTowerActions.ADD_GROUP:
return newState = {
...state,
groups: { ...state.groups, [payload.groupId]: payload.group }
};
break;
So, my question is: Do I need to clone deep the state before save it on this.groups prop? this.groups = newState.groups;
I think that every time I change the redux state, I return a new state object so there won't be a problem with my local state(this.groups) pointing to the old state.
But I just want to make sure I am not following any anti pattern.
Regards,
Official Redux docs says:
State is read-only
The only way to change the state is to emit an action, an object describing what happened.
This ensures that neither the views nor the network callbacks will ever write directly to the state. Instead, they express an intent to transform the state.
You can view full list of core principles here https://redux.js.org/introduction/three-principles
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.