So i'm fairly new to React and I can't wrap my head around a concept on how to re-render a main component based on another component.
Lets say we have a to-do application and a to-do item can have a state (new, running, closed). We are displaying all to-do items in a list and we can filter the items based on their state. We are using a bootstrap dropdownbutton like component to set the filter, which is a React component. Now when we change the filter we obviously want to refresh the to-do items.
My question is, does the logic of the selected state belong in Flux/Redux or does the filter component just say "refresh your items" to the main component?
When you use Redux in React application, follow one simple rule - all your components are stateless (means, no component initializes its state or calls .setState() anywhere).
The redux way of design based on state container, one big object that holds all application state. As React component, being connected to Redux store, Redux will pass the state (or portion of it) into that component, as this.props.state property.
That high-order component (or smart component), renders its children components (or dumb components) and handles events from them.
If child component requires change, it triggers corresponding handler (which typically passed as props). The only way to change the state is to dispatch an action. That action contains a type and a payload and based on that type, corresponding reducer is selected. The reducer then produces a new state, based on previous state and action payload.
If in the result of reducer call, state got changed, Redux will re-render high-order component, passing new state in properties. So, all child components will be updated correspondingly.
Check this example and trace how AddTodo component calls .handleClick() and then upper to .onAddClick() which dispatches an action.
If you are using redux, then on your dropdown you should have an onchange handler that dispatches an action every time the value is changed with the selected state (new, running, closed).
Redux reducer will handle this action by changing some state accordingly in the store for example: display = 'completed'. In addition to this, your todo list should also be stored in the store and it will likely be an array.
Your react component should receive a the todo array and display as props, and therefore everytime any prop (todo array or display) change, it will trigger a re-render.
In this case, your component should only display those todos that are complete (i.e. in the render you check if the state of each todo === this.props.display.
So to answer your question: redux keeps the state of the dropdown, which is passed to your main component, your main component then render only the todo's that matches the criteria.
So in a very minimal way, you could pass a function down to the select box, which calls setState on the top-level component. When that state changes, it will re-render its child components. This pattern doesn't scale well, and leads to the same hell React + Flux is trying to get us away from (state everywhere). I would say, using Flux (or even better, Redux), trigger an action that sets the filter in your store, and use derived data based on the filter state + your list of todo's in the todo list.
Related
I want to store the value of fields, upon update, in a redux state property. Is there a way to do this globally for all the form data or do I need to dispatch a separate reducer for each controlled component upon onChange/onValueChange/onTextChange?
By the way, I'm working in React Native, if that matters (thus using some specific patterns such as control instead of register).
Extra info about my form and redux state:
my form has nested children, each containing various custom controlled components. In each of those controlled components, the local form state is updated accordingly (either by passing the new value to setValue if it's a custom input).
After getting my local form data updating properly, I persisted the state in redux by dispatching a reducer in onSubmit. Because this form creates data associated with one item from a list of items, the reducer stores the form data in a parent object located at state.listOfItems[index].formData.
In a Todo app made with React, like this one, we have a function toggleTaskCompleted in the App component which gets passed as a prop to each Todo component. This function can then be called in the Todo component when a button is clicked resp. a checkbox is toggled.
I wonder if we can move this logic entirely to the Todo component. For example, this is possible in Svelte and Vue. In Vue, we create a ref for the list (and in Svelte, a regular variable), loop through it to list all todos and pass the respective todo as a prop. When we change the todo in the Todo component (for example, mark it as complete), this change is automatically also seen by the parent App component. (Meanwhile, the approach by passing a function as a prop is also possible in Svelte and Vue.)
I prefer this approach much more, it is more encapsulated, and we have to write less code. So I wonder if we can do the same in React.
Edit. Actually, the list is updated in the App component. However, this does not effect a rerender, and also useEffect, depending on the list, won't notice this change. So my question is basically how to inform the component about this change (without writing too much code or even using external state mangement).
I guess it would be better to use React Context in this case because you'll need to applay the state and the logic for your Todos in many components (like a navbar to track number of todos ...) . In this case React Context is a way to manage state globally. So it allow your components to access some global data (In your case your todos and the function toggleTaskCompleted ) and and re-render when that global data is changed . this is a simple Demo how to use it
Isn't that a very expensive affair?
For example, if I clicked a button that toggles something... Does React need to recreate the entire Virtual DOM just for that one action and diff it as well?
When you call React Redux's connect on components, you're wrapping them in a component called Connect. The component reads the store from the <Provider>'s context. When you dispatch an action, the Redux store gets updated, which causes all connect-ed components to get new props (those defined in mapStateToProps during a store update.
If a connected component subscribes to a store field but the action doesn't change that field's value, it wouldn't re-render.
The rest follows regular React rendering rules. If a connected component's HOC wrapper subscribes to a store field that has changed, it will update and cause the connected component to re-render as well. You could implement shouldComponentUpdate in the connected component if you don't want it to re-render.
References:
https://github.com/reduxjs/react-redux/blob/master/src/components/Provider.js
https://github.com/reduxjs/react-redux/blob/master/src/components/connectAdvanced.js
I'm not sure it is default behaviour or redux or something else but i found that on dispatching an action, this action traverse through all reducers(that's ok) but it also invoke the connect listener of every reducer that further resulting rendering of its component. This means on every dispatch, all component inside app state tree get rendered. Is this intentionally done by redux or i've done something wronge.
Help me out to clarify this things.
In Redux , your state is global and handled by the redux, whenever you dispatch an action , you are just setting the global state. Your container comonents will receive the new state and reducer would work on them but your components wont be rerendered since previous state and next state would be same.
Only those components would be rendered whose mapStatetoProps result in a different result
This behavior is totally fine. See the React Docs for their virtual DOM concept:
React makes use of a virtual DOM, which is a descriptor of a DOM subtree rendered in the browser. This parallel representation allows React to avoid creating DOM nodes and accessing existing ones, which is slower than operations on JavaScript objects. When a component's props or state change, React decides whether an actual DOM update is necessary by constructing a new virtual DOM and comparing it to the old one. Only in the case they are not equal, will React reconcile the DOM, applying as few mutations as possible.
So you don't have to worry that every component will get re-rendered every time you dispatch an action.
I have a YouTubeiFrame react component that renders an empty div and then drops the YouTube iframe player in to that div using componentDidMount. All works fine. I have a cueVideo method defined on the component which uses the players
API to cue new videos:
cueVideo(videoId) {
this.player.cueVideoById(videoId)
}
In different places in my app you can click a button or some another action to indicate that you want to play a new video. When you perform this action the 'currentVideo' state is updated via redux and as a result the YouTubeiFrame component receives the new video id as an updated prop.
My question is how to call the cueVideo method above in reaction to the updated prop. I've considered using shouldComponentUpdate to compare this.props.currentVideo with the next props
but concious this method is really supposed to return true/false so the render function can be called. All I want to do is call cueVideo when the currentVideo prop changes - I don't really need to re-render.
What's the best way to do this?
All I want to do is call cueVideo when the currentVideo prop changes - I don't really need to re-render.
When new props are received, React will automatically call render() to update your component. Once all the updates have been flushed to the DOM, React will also call componentDidUpdate() on your component to give you an opportunity to respond to the changes. As the documentation says:
Use this as an opportunity to operate on the DOM when the component has been updated.
I would suggest this is the perfect place to call your code as needed.