Bind react component to part of redux state - javascript

I have redux store that looks something like this:
{
user: {},
alerts: [],
reports: [],
sourses: []
}
For each one of this parts of state i have a bunch of React Components wrapped in a container wich connected via react-redux. And has mapStateToProps like this
(state) => {alerts: state.alerts}
(state, ownProps) => {alert: _.filter(state, {id: ownProps.curId})}
Problem that when i for example launch some action for Alerts like CREATE_ALERT or EDIT_ALERT and redux state updated, ALL REACT COMPONENTS WILL RESPOND TO THIS CHANGE even ones that works with different parts like sources or reports.
My question: how to "bind" certain components to certain parts of a tree. So each container component WILL UPDATE ONLY WHEN APROPRIATE PART OF REDUX STATE UPDATED and ignore other changes.
Expected behavior
Dispatch CREATE_ALERT -> Alert reducer -> Redux store update -> ONLY Alert container component re-rendering.

When you are changing state in redux the whole state becomes just a new object.
Then your component is given by this new object (new reference) and re-renderes itself.
To fix this behaviour you need to add some logic to compare if your component got props with different value (not reference).
The easiest and fastest way is to use React.PureComponent. You can also override shouldComponentUpdate function and handle changes by yourself. But note that PureComponent works only with primitives (it does a shallow compare).
Check also Immutable.js which helps you with intelligent way of changing references of props.

if you use connect method, then pass only selected redux state to the component, this will prevent rendering of other components
example:
User Component:
const mapStateToProps = state =>({
users: state.users
});
export default connect(mapStateToProps)(User)
Alert Component:
const mapStateToProps = state =>({
alerts: state.alerts
});
export default connect(mapStateToProps)(Alert)

Check this out: Avoid Reconciliation
There explains what Neciu says.

Container components created with connect will always receive notifications of all updates to the store.
The responsibility for consuming these updates falls on the receiving connect component. It should contain the logic to extract the data relevant to it.

Related

React classes in main component constructor

Let's say I have a lot of app state to manage in my React application.
Therefore, I would like to split the state into smaller, manageable chunks.
For example I have the following main component with state and methods that alter this state.
class App extends Component {
constructor(props) {
super(props);
this.state = {
foo: ['some', 'items'],
bar: [{ arr: 'of objects'}]
}
}
changeFoo() {some code in here...}
changeBar() {some code in here...}
}
The state and methods written in the App component are getting out of hand. Yet it must be written in the App component since the state is passed to other components as props.
How would you usually manage this?
When you see that the state of your React application is getting out of hand, it's usually time to bring in a state management library like Redux (there're a few and Redux is the most popular one).
It'll help you have a global state that is managed in a reasonable way.
When we see how React works. It is based on one-directional data flow.
So, usually the Application state is kept at the top most Component (Say, App Component) in your case. So that data/state can be passed down as props to the component that needs it.
There, however may be the cases where children components of the parent, needs to work with the same data(Say in case of an event - a button click that happens in the child component.) In that case we write a function in the parent component and pass the function as props to the children, so that the state gets updated in the parent itself and any child gets updated data.
In pure React (without using any state management library), we have to pass the state as props to work with our app. But in case you choose to use a state management library such as Redux, then the components (known as Containers) can directly communicate with the Application State.
And If your application state contains objects within objects(like you have shown) or Array of Objects containing more Objects, then you cannot use setState() to update the state directly. In most of the cases, you take copy of the state and then use JSON.parse(JSON.stringify(state)) to do deep cloning and work with the state in a best possible manner.
There are other things in the example, the functions that you have used within the class , you need to bind the scope of this variable to point to the current class. This we do inside the constructor method, or simple make use of arrow function in order to avoid errors.
If you need more explanation, I will share with you :)
One solution is to make a generic change() function with a parameter for the key that should be changed:
change(key, value) {
setState({key: value, ...this.state});
}
Now when you want to add a listener to a child component:
<Foo onChange={ value => change('foo', value) }/>
<Bar onChange={ value => change('bar', value) }/>

Need some clarifications on some basic setup

After watching a few Redux tutorials I still have a few questions:
After creating my store, i have:
ReactDOM.render(, document.getElementById('root'));
Going into the App component, I define:
function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actions, dispatch)
}
Where do state and dispatch come from? Is this automatically referring to the store's state and dispatch since i connected it to my component?
One of my React components has a form which on submit, calls a function:
handleSubmit(event) { ... }
So in the constructor of that component, I have:
constructor() {
super()
this.handleSubmit = this.handleSubmit.bind(this);
}
Is calling super() always necessary when declaring a React class component?
Why do I need to do this type of binding there?
After submitting that form, I dispatch an action called addPost.
How does it "go" to the reducer? Is it just because the reducer was given when I created the store and using mapDispatchToProps(dispatch) I "let" Redux know which actions could be dispatched to that reducer?
The reducer simply returns a state object. Where is the logic that actually "saves" that state into the store? Is it hidden?
Yes, treat state and dispatch as references to your redux state and the dispatch function respectively.
React docs say:
If you don’t initialize state and you don’t bind methods, you don’t need to implement a constructor for your React component.
Try an arrow function for your handler:
class MyComponent extends Component {
handleSubmit = () => {
// ...
}
}
The reducer was given to the store, the store was given to the react-redux <Provider> component, and the Provider component provides a React context where dispatched actions in descendant components get picked up by said reducers.
That logic is in the redux library.
Please check out our new official React-Redux docs . Specifically, you should read through connect: Extracting Data with mapStateToProps and connect: Dispatching Actions with mapDispatchToProps.
If you define a constructor for a class, and it extends another class, then yes, you need to call super() as the first line of the constructor - that's how ES6 classes are defined to work.
Redux only has a single reducer function. The insides of dispatch() look like this:
function dispatch(action) {
newState = rootReducerFunction(oldState, action);
subscriberCallbacks.forEach(callback => callback() );
}
You might want to read through the Structuring Reducers page in the Redux docs to get a better understanding of how and why reducers are normally split into smaller functions.
In most cases, the logic that combines the different "state slices" back together is in combineReducers(), because you used it to generate the "root reducer" function.
Where do state and dispatch come from? Is this automatically referring to the store's state and dispatch since i connected it to my component?
State is one of the hardest to understand topics and comes in front and center when we start concentrating on Redux. Each class-based component that we define has its own state object.
mapStateToProps is our ability to interface from the application level state down to the component level. Its where we sort of pluck properties off our state object and inject them into our components.
dispatch is how you change the state in your application, you dispatch an action.
In your implementation, you are using bindActionCreators, which turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into a dispatch call so they may be invoked directly.
I personally have not ever connected redux actions this way, so I had to look this one up.
Is calling super() always necessary when declaring a React class component? Why do i need to do this type of binding there?
Yes, React components always have to call super in their constructors to be set up properly.
For an in-depth explanation of this kind of binding trickery, begin with the React docs. If you are not so inclined, just know that in React, whenever you define an event handler that uses this, you need to add this.methodName = this.methodName.bind(this) to your constructor function.

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.)

react, redux - Modifying parent components with redux

Okay so I had a problem when programming in react, and I've found that it's a common one. If I have multiple nested components, in my case I have:
<AppView>
<Navigation/> // this is a navbar
<ViewHandler currentTab={props.currentTab}/>
<Footer/>
</AppView>
And then in <ViewHandler/> I have other dumb presentational components, which also have nested components as well. If I have a button in a deeply nested component within <ViewHandler>, and I want to respond to onClick from that button by changing something many parent components above the component that I am in, how would I do so? In my case I would be reacting to the button being clicked in that deeply nested component, and then I want to change the selected tab on <Navigation>. I don't want to pass a bunch of callback functions down as properties, because that feels very scotch-tape-ish.
I learned redux because I read that it solved this problem. But for me it hasn't. I am giving <AppView> access to my redux store using react-redux's <Provider>, and I can access the store through props (props.currentTab). But for all the components nested within <AppView>, they don't have access to the store or any of my action creators. How can modify my store from within a deeply nested component so that I may change a parent component without passing a ton of callback functions down? Or is this just incorrect architecture? I thought redux would solve this problem but it hasn't.
Yes I have connected my component. I just don't like the idea of passing down store.state information as props because it gets very redundant with many nested components.
I don't know why you think you have to send props all the way down your component tree. That's what connect and mapStateToProps help you avoid: they let you turn bits of app state into props only for the components which need it.
in your button's onClick handler, create and dispatch a Redux action:
// button.js
onClick={() => {
dispatch({
payload: 1 // or whatever value
type: 'SET_SELECTED_TAB'
});
}}
next, have your reducer function watch for this action and modify a bit of Redux app state:
// reducer.js
if (action.type === 'SET_SELECTED_TAB') {
return {
...currentAppState,
selectedTab: action.payload
};
}
finally, in the render function of your <Navigation> component, you decide which tab to show based on the current values in that bit of app state:
// Navigation.js
render() {
return (
<div>
current tab: {this.props.selectedTab}
</div>
);
}
access to that state is via connect and mapStateToProps:
// Navigation.js still
const mapStateToProps = (appState) => {
return {
selectedTab: appState.selectedTab
};
};
export default connect(mapStateToProps)(Navigation);
Hoc (higher order components) is a wrapper that is serving methods and data to the children components, usually it's a good idea to use it , but it enforces some 'discipline'.
Example: if your HOC is at level 0 and you have a deeply nested button component at level 4 that calls a method in this same HOC , What should you do ? pass it down the to all 4 levels? the answer is NO WAY !
Because doing so will bring the spaghetti to it , Everytime you click this button , and assuming the method binded to it will mess with the state (internal or the store itself) it will rerender all the 4 levels , and you could avoid that by using the shouldComponentUpdate() but this is way too much work for nothing useful.
So the solution would be to connect every component with mapStateToProps and mapDispatchToProps , right ?
well kind of , in fact after using extensively react and redux , you will notice that for every component , there is a sweet spot in terms of size , childrens , and what you should put in it and what you should not.
Example: you have a button inside a form that controls the send mechanism , there's no need to make a component for the button , it will add up complexity without any benefit. just put it on the form component and you will have both ready to use.
If you really need to call actions or to pass props between a deeply nested component and an HOC then use the connect module at the component level (for your case the button) , but not much because it will make your components heavier (to load and to display).Here are some tips to help :
you need to be as specfic as possible when you use mapStateToProps , don't return the whole store , just the piece of data needed , same for mapDispatchToprops , just bind the method that you will be using nothing else.
in your case the button doesn't have to know which tab is selected , so a mapDispatchToProps is enough.
avoid deep nesting components that handles some kind of logic ,refactor your structure or create A HOC for that component , logic less components in contrary can be nested deeply
If you are writing a huge app with a lot of reducers and states , consider using selectors , and some libraries like reselect.
I know that this is not the answer you were expecting but following this guideline will saves you countless hours of refactoring.
Hope it helps

redux state selectors in top level reducer

After watching the new egghead course by Dan Abramov, I have question regarding the selectors that was mentioned.
The purpose of the selectors is to hide the details of the state tree from the components, so that it is easy to manage code later if tree changes.
If I understand it correctly, that means, the selectors called inside mapStateToProps should only be the ones that live in the top-level reducer. Because the state that is passed to mapStateToProps is the whole application state tree. If this is true, as the application grows, I can imagine it would become very difficult to manage the top level selectors.
Have I miss understood the concept here? or is this a valid concern?
Edit: trying to make my question clearer.
Say my whole state start with
{ byIds, listByFilter } and I have
export const getIsFetching = (state, filter) =>
fromList.getIsFetching(state.listByFilter[filter]);
in my top level reducer reducers/index.js, and components would simply use getIsFetching passing the whole state to is, which is totally fine because it is the top level.
However, later on, I decided my whole app is going to contain a todo app and an counter app. So it make sense to put the current top level reducers into reducers/todo.js, and create a new top level reducers reducers/index.js like this:
combineReducers({
todo: todoReducer,
counter: counterReducer
})
at the point my state would be like
{
todo: {
byIds,
listByFilter
},
counter: {
// counter stuff
}
}
components can no longer use the getIsFetching from reducers/todo.js, because the state in getIsFetching is now actually dealing with state.todo. So i have to in the top level reducer reducers/index.js export another selector like this:
export const getIsFetching = (state, filter) =>
fromTodo.getIsFetching(state.todo);
only at this point, the component is able to use getIsFetching without worring about the state shape.
However, this raises my concern which is all the selectors directly used by components must live in the top-level reducer.
Update 2: essentially we are exporting selectors from the deepest level all the way up to the top-level reducers, while all the exports in the intermediate reducers are not using them, but they are there because the reducer knows the shape of the state at that level.
It is very much like passing props from parent all the way down to children, while the intermediate component aren't using props. We avoided this by context, or connect.
apologize for the poor English.
So while mapStateToProps does take the entire state tree, it's up to you to return what you'd like from that state in order to render your component.
For instance, we can see he calls getVisibleTodos and passes in state (and params from the router), and gets back a list of filtered todos:
components/VisibleTodoList.js
const mapStateToProps = (state, { params }) => ({
todos: getVisibleTodos(state, params.filter || 'all'),
});
And by following the call, we can see that the store is utilizing combineReducers (albeit with a single reducer), as such, this necessitates that he pass the applicable portion of the state tree to the todos reducer, which is, of course, state.todos.
reducer/index.js
import { combineReducers } from 'redux';
import todos, * as fromTodos from './todos';
const todoApp = combineReducers({
todos,
});
export default todoApp;
export const getVisibleTodos = (state, filter) =>
fromTodos.getVisibleTodos(state.todos, filter);
And while getVisibleTodos returns a list of todos, which by is a direct subset of the top-level state.todos (and equally named as such), I believe that's just for simplicity of the demonstration:
We could easily write another perhaps another component where there's a mapStateToProps similar to:
components/NotTopLevel.js
const mapStateToProps = (state, { params }) => ({
todoText: getSingleTodoText(state, params.todoId),
});
In this case, the getSingleTodoText still accepts full state (and an id from params), however it would only return the text of todo, not even the full object, or a list of top-level todos. So again, it's really up to you to decide what you want to pull out of the store and stuff into your components when rendering.
I also came across this issue (and also had a hard time explaining it...). My solution for compartmentalization this follows from how redux-forms handles it.
Essentially the problem boils down to one issue - where is the reducer bound to? In redux-forms they assume you set it at form (though you can change this) in the global state.
Because you've assumed this, you can now write your module's selectors to accept the globalState and return a selector as follows: (globalState) => globalState.form.someInnerAttribute or whatever you want.
To make it even more extensible you can create an internal variable to track where the state is bound to in the global state tree and also an internal function that's like getStateFromGlobalState = (globalState) => globalState[boundLocation] and uses that to get the inner state tree. Then you can change this variable programatically if you decide to bind your state to a different spot in the global state tree.
This way when you export your module's selectors and use them in mapStateToProps, they can accept the global state. If you make any changes to where the where the reducer is bound, then you only have to change that one internal function.
IMO, this is better than rewriting every nested selector in the top level. That is hard to scale/maintain and requires a lot of boilerplate code. This keeps the reducer/selector module contained to itself. The only thing it needs to know is where the reducer is bound to.
By the way - you can do this for some deeply nested states where you wouldn't necessarily be referring about this from globalState but rather some upper level node on the state tree. Though if you have a super nested state it may make more sense to write the selector from a upper state's POV.

Categories

Resources