I am struggling a bit with the concept of global state and reusable components in redux.
Let's say I have a component that's a file-selector that I want to use in multiple places inside my applications state. Creating action/reducers leads to a lot of bloat as I have to handle states with dynamic suffixes and other weird things that just don't really strike me as a smart way to go about things.
What's the general consensus on these things? I can only see two solutions:
Make the file-selector component have local state (this.setState/this.getState)
Make the file-selector be part of the global state but in it's own unique reducer that I can read from once the operation of the component is done?
Any ideas / best practices? Thanks.
Update: To clarify the file selector I am describing is not a simple component that works purely on the client side but has to fetch data from the server, provide pagination as well as filtering etc.. That's why I'd also like to reuse most of the client/server interaction. The views that display this component are of course dumb and only display values from the state - but how do I reuse the actions/reducers in multiple places around the application?
Have your reducer handle multiple instances of your component state. Simply define some "unique" ID for each instance of your FileBrowser component when it appears in the app, and wrap your current state in an object with this uniqueIds as keys, and your old complex state as value.
This is a technique I've used multiple times. If all your FileBrowser are known at compile time, you can even setup the initial state before running your app. If you need to support "dynamic" instances, simply create an Action that initializes the state for a given id.
You didn't provide any code, but here's a contrived example for a reusable Todo reducer:
function todos(state={}, action){
switch(action.type){
case 'ADD_TODO':
const id = action.todoListId
return {
...state,
[id]: {
...state[id],
todos: [ ...state[id].todos, action.payload ]
}
}
// ...
}
}
Usually, the rule of thumb is that you use a redux store to manage data in your application aka storing items fetched from the server and local react state for ui behaviors, like file uploads in your case. I'd make a pure react component to manage file uploads and then use redux-form to manage specific form.
Here is the example of the component I use in my project
import React, {Component, PropTypes} from 'react';
import Button from 'components/Button';
class FileButton extends Component {
static propTypes = {
accept: PropTypes.string,
children: PropTypes.any,
onChange: PropTypes.func.isRequired
};
render() {
const {accept, children, onChange} = this.props;
return <Button {...this.props} onClick={() => this.file.click()}>
<input
ref={el => this.file = $(el)}
type="file"
accept={accept}
style={{display: 'none'}}
onChange={onChange}
/>
{children}
</Button>;
}
}
export default FileButton;
We came to the conclusion that reusable components must be of two kinds:
dumb components, i.e. components that only receive props and trigger "actions" via props callbacks only. These components have minimal internal state or at all. These are the most frequent of reusable components, and your file selector will probably fall in that case. A styled Text Input or custom List would be good examples too.
connected components that provide their own actions and reducer. These components have their own life within the application and are rather independent from the rest. A typical example would be a "top error message box" that displays on top of everything else when the application fails critically. In such a case the application triggers an "error action" with the appropriate message as payload and on the following re-render, the message box displays on top of the rest.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am displaying contact list array.
If contacts array is empty it will show the loader else will display contact list.
I render loader using higher order component Using this tutorial. For this I got performance summary as shown in the below image-
when i render loader using simple if else condition then i had performance graph as below -
comparing this i got to know that while using higher order component it requires more time than simple loop.
Can anyone please explain me which is better to use ? and when we should use the Higher order component ? what are advantages and disadvantages of using Higher order component ?
A higher order component allows you to apply functionality or data that is common amongst a number of components to a component and will return a new component with the functionality or data of the HoC included.
As for advantages or disadvantages. Would entirely depend on the circumstance and problem you are trying to address. You would also need to perform a long list of tests as the times are too close to really say one is quicker than the other. Also this looks like it is on your local so on production server could be an entirely different story.
But in your circumstance are you apply any extra functionality to your <Loader /> component? If you aren't or that functionality is not going to be used anywhere else then it might be unnecessary to use a HoC in your case.
ReactJS - Higher Order Components
pros for HOC:
extremly reusable around your app/projects:
// withShowSupport.js
export default Component => props =>
!props.show
? null
: <Component { ...props } />
easy testing
res:
https://www.robinwieruch.de/gentle-introduction-higher-order-components/
New technology can lead us to be more efficient and effective in productivity. But, of course, it will also increase resource usage.
I don't have enough knowledge to explain more, but I can give you simple study case.
I have a react app which consists of several pages (how about > 10 pages?). I want to do something when each page has been rendered (In other words, page changes). If-else condition rendering? No way!
Actually, you can make an HOC named withBase that will wrap each page (Use it when you export your page component e.g. export default withBase(Home)). withBase will execute a function that indicates page has been changed.
DEMO
As per definition :
HOC is a function that accepts your component as an argument and
returns a new function that adds some feature to the component.
A simple use case is - when you call an API to fetch some data and use them to render some content in your application, we need to show some kind of progress bar, loading indicator, etc to tell users that data is being fetched. We can use HOC for that purpose.A sample code goes here
import React from 'react';
import List from './List.js';
import WithLoading from './WithLoading.js';
const ListWithLoading = WithLoading(List);
class App extends React.Component {
state = {
loading: false,
repos: null
}
componentDidMount() {
this.setState({ loading: true });
fetch(`https://api.github.com/users/farskid/repos`)
.then(json => json.json())
.then(repos => {
this.setState({ loading: false, repos: repos });
});
}
render() {
return (
<ListWithLoading isLoading={this.state.loading} repos={this.state.repos} />
)
}
}
the above is App.js file and below is the HOC component WithLoading
import React from 'react';
function WithLoading(Component) {
return function WihLoadingComponent({ isLoading, ...props }) {
if (!isLoading) return (<Component {...props} />);
return (<p>Be Hold, fetching data may take some time :)</p>);
}
}
export default WithLoading;
so in App.js, we are calling HOC like const ListWithLoading = WithLoading(List); and <ListWithLoading isLoading={this.state.loading} repos={this.state.repos} />, Where List is the component which is passing into the HOC. based on the sLoading prop, loding symbol is shown. Another one interesting thing is HOC dont have a render menthod! beacuse its a simple function !
By my understanding, Higher-Order-Component adds an additional layer between components or libraries for better code-reuse.
For example, you have a library file "User.class.js" that is responsible to retrive/store user data, and it's used by different components. When "User.class.js" is modified, method names changed, or parameters changed. What you gonna do? Apply changes to all those components? What if it's used by 100 different components, or even more? With Higher-Order-Component, you just need to apply the changes to one single file.
Let's say at the top of the app, we retrieve some basic information about the app or user before rendering the rest of the application:
const getUser = gql`
query getUser(id: Int!) {
user(id: $id) {
id
name
}
}
`)
function App({ data }) {
return (
<div>
{!data.loading && !data.error && (
// the application
)}
</div>
)
}
export default graphql(getUser, {
options: (props) => ({ variables: { id: props.id }})
})(App)
Now anywhere in the application, it is safe to assume that the user has been loaded and is stored. What is the proper way for another deeply nested component to the retrieve the user data without having to redo the querying and loading logic?
This is the very basic use of a store-based library like Redux. This is not the purpose to guide every step of the way here but you are looking for a single source of truth as described here: http://redux.js.org/docs/introduction/ThreePrinciples.html
In short:
Receiving getUser response should trigger a 'LOGGED_IN' action dispatching user Data, this would be catched by a reducer updating the user object in your store (as much nested as you want), a container would then connect to this user in the store and have all its data using connect()
As of now, I'm not certain there is a proper way, but these are the options I think are reasonable
Manually pass down data via props
Wrap your deeply nested component with the same query
Manual pass down ensures your components rerender correctly, but it can be a pain to refactor. Wrapping your nested component would just hit the cache. Yes, you probably need to redo the loading logic, but that's not a show stopper.
My advice is to manually pass down props for shallow nested components and rewrap deeply nested components. Unfortunately, react-apollo doesn't provide a convenient way to access the apollo-store for nested components the same way that redux's connect container does.
I am realatively new in React JS. Few weeks ago I created To Do List app in JS, jQuery and now I am going to rebuilt it using React, just for change my point of view and practice React.
I have few components (siblings) in different files and one parent component - App, components:
App:
- Navigation
- Task List
- Add Task
- Footer
How can my navigation component communicate with task list component?
To be more specific I want to have something like global variable selectedDay and use it in all components.
When user choose in Navigation component single day, for example Sunday , I want to save "sunday" in this variable and later use it in Task List (this is of course sample example of data). My question is how to store data in first component and use it in another one?
Should I use state for this kind of purposes? I was thinking about set initial state in parent (App) component -> selectedDay : "monday" /default/ and later update it by Navigation component and use in Task List component. Could you help me, please? I will be gratefull!
There are two solutions for this.
1- Use a library that handles a global state, like Redux (as FurkanO said). That way, your "big components" (aka containers) are connected to the global state of your application, and update it with actions.
Actions are some kind of events with a type, and sometimes a payload, that will be intercepted by a reducer and trigger a state update.
2- Use the state of the lowest common parent of the components you want to see interracting.
Basic Example for 2- : Parent Component contains Navigation & TaskList.
class Parent extends Component {
state = {
selectedDay: defaultDay,
}
setDay = (selectedDay) => {
this.setState({ selectedDay });
}
render () {
const { selectedDay } = this.state;
return (
<div>
<Navigation setDay={this.setDay} />
<TaskList selectedDay={selectedDay} />
</div>
);
}
}
Then you just use this setDay function in your Navigation component to set the state in the Parent Component. That way, your TaskList will receive the new value via its props.
This method has its limits (it really doesn't scale well in my opinion).
Hope that helped. Please tell me if this isn't clear for you.
What you need is redux. It provides you a global state tree which is an object, a way to manipulate it and most importantly whenever your state tree changes rerenders your components. So that your components are always up-to-date with updated state tree.
As I get further into implementing redux + react into a fairly complex app which depends on many API requests to load a single page, I'm having trouble deciding whether it's better to have a single container component at the root of the page which handles all async stuff and passes props down to dumb components, v.s. having multiple container components which concern themselves only with the data they need, as well as fetching the data they need. I've gone back and forth between these two patterns and found that they each have pros/cons:
If I put a single container component at the top:
pro: All isFetching props and fetchSomeDependency() actions can be handled in one place.
con: the downside which is really annoying is that I find myself having to forward props and callbacks through multiple components, and certain components in the middle of the tree end up being tied up to this.
Here's a visual example of the issue that shows the relationships required props-wise:
<MyContainer
data={this.props.data}
isFetchingData={this.props.isFetchingData}
fetchData={this.props.fetchData}
>
{!isFetchingData &&
<MyModal
someData={props.data}
fetchData={props.fetchData}
>
<MyModalContent
someData={props.data}
fetchData={props.fetchData}
>
<SomethingThatDependsOnData someData={props.someData} />
<SomeButtonThatFetchesData onClick={props.fetchData} />
</MyModalContent>
</MyModal>
}
</MyContainer>
As you can see, <MyModal /> and <MyModalContent /> now need to be concerned with props that have nothing to do with it, seeing as a modal should be able to be re-used and only be concerned with stylistic qualities of a modal.
At first the above seemed great but once I got to 100+ components it all felt very tangled, and I found the complexity of these top-level container components to be too high for my liking, seeing as most of them (in the app I'm working on) depend on responses from 3+ API requests.
Then I decided to try multiple containers:
pro: Completely removes the need to forward props. It still makes sense to do it in some cases, but it's a lot more flexible.
pro: Way easier to refactor. I'm surprised at how I can significantly move around and refactor components without anything breaking, whereas in the other pattern things broke a lot.
pro: The complexity of each container component is much less. My mapStateToProps and mapDispatchToProps is more specific to the purpose of the component it's in.
con: Any component that depends on async stuff will always need to handle isFetching state in itself. This adds complexity that is not necessary in the pattern where its handled in a single container component.
So the main dilemma is that if I use one container, I get this un-necessary complexity in components between the root container and the leaf components. If I use multiple containers, I get more complexity in the leaf components, and end up with buttons that need to worry about isFetching even though a button should not be concerned about that.
I'd like to know if anyone has found a way to avoid both cons, and if so, what is the "rule of thumb" you follow to avoid this?
Thanks!
The way I have always seen it is to have your containers at the top most component of a logical components group other than your root/app component.
So if we have a simple search app that display results and lets assume the component heiarchy is such
<Root> <- setup the app
<App>
<Search/> <- search input
<Results/> <- results table
</App>
</Root>
I would make Search and Results redux aware containers. Because react component are suppose to be composable. You might have other components or pages that need display Results or Search. If you delegate the data fetch and store awareness to the root or app component, it make the components become dependent on each other/app. This make it harder down the line when you have to implement changes, now you have to change all the places that use them.
The exception to this is probably if you do have really tightly coupled logic between components. Even then, I would say then you should create a container that wraps your tightly coupled components since they won't be abled to be used realistically without each other.
Redux author Dan Abramov suggests that you use container components when you need them. That is, once you get to have too many props wiring up and down between components it's time to use containers.
He calls it an "ongoing process of refactoring".
See this article: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
I wouldn't even consider using a single container approach. It pretty much entirely negates all advantages of redux. There is no need whatsoever to have a state management system if all your state is in one place and all your callbacks are in one place (root component).
I think there's a thin line to walk, though. I'm making an app where I've been at it for about 5 weeks (part time) and it's up to 3000 lines right now. It has 3 levels of view nesting, a routing mechanism i implemented myself, and components that are 10+ levels of nesting deep that need to modify state. I basically have one redux container for each big screen and it works marvelously.
However, if I click on my "clients" view, I get a clients listing which is fine, since my clients view is inside a redux container and gets the list of clients passed as props. However, when I click on one client, I'm really hesitant to do another redux container for the individual client's profile since it's only one additional level of passing props. It seems that depending on the scope of the app, you might want to pass props up to 1-2 levels past the redux container and if it's any more than that, then just create another redux container. Then again, if it's an even more complex app, then the mixing of sometimes using redux containers and some other times not using them could be even worse for maintainability. In short, my opinion is trying to minimize redux containers wherever possible but definitely not at the expense of complex prop chains, since that's the main point of using redux to begin with.
So it's been over 2 years since I've posted this question, and this whole time
I have been consistently working with React/Redux. My general rule of thumb now
is the following: Use more containers, but try to write components in such a way where they don't need to know about isFetching.
For example, here is a typical example of how I would have built a to-do list before:
function Todos({ isFetching, items }) {
if (isFetching) return <div>Loading...</div>
return (
<ul>
{items.map(item =>
<li key={item.id}>...</li>
)}
</ul>
)
}
Now I would do something more like:
function Todos({ items }) {
if (!items.length) return <div>No items!</div>
return (
<ul>
{items.map(item =>
<li key={item.id}>...</li>
)}
</ul>
)
}
This way, you only have to connect the data, and the component has no concerns about states of asynchronous API calls.
Most things can be written this way. I rarely need isFetching, but when I do it is typically because:
I need to prevent, for example, a submit button from being clicked a second time, which makes an API call, in which case the prop should probably be called disableSubmit rather than isFetching, or
I want to explicitly show a loader when something is waiting for an asynchronous response.
Now, you might think, "wouldn't you want to show a loader when items are being fetched in the above todos example?" but in practice, actually I wouldn't.
The reason for this is that in the above example, let's say you were polling for new todos, or when you add a todo, you "refetch" the todos. What would happen in the first example is that every time this happened, the todos would disappear and get replaced with "Loading..." frequently.
However, in the second example that is not concerned with isFetching, the new items are simply appended/removed. This is much better UX in my opinion.
In fact, before posting this, I went through all the UI code for an exchange interface I wrote which is quite complex and did not find a single instance of having to connect isFetching to a container component that I wrote.
You don't have to dispatch AND load your state in the same place.
In other words, your button can dispatch the async request, while another component can check if you're loading.
So for example:
// < SomeButtonThatFetchesData.js>
const mapDispatchToProps = (dispatch) => ({
onLoad: (payload) =>
dispatch({ type: DATA_LOADED, payload })
});
You'll need to have some middleware to handle a loading state. It needs to update isFetching when you're passing an async payload.
For example:
const promiseMiddleware = store => next => action => {
if (isPromise(action.payload)) {
store.dispatch({ type: ASYNC_START, subtype: action.type });
Then you can use it wherever you want:
// <MyContainer.js>
const mapStateToProps = (state) => ({
isFetching: state.isFetching
});
And load the data in your inner nested component:
// <SomethingThatDependsOnData.js>
const mapStateToProps = (state) => ({
someData: state.someData
});
I have quite a complex multi-level deep/nested form connected to React/Redux using reduxForm(). As the form is very large, I've broken parts of it into child components. Some of these components require access to the Redux state, so need to either have connections passed down via props or be connect()ed. However when I use the normal Redux connect() function to connect the child components, it becomes impossible to edit the redux-form fields within them. So I tried using reduxForm() to connect the child components as well as the parent, which felt wrong, but appeared to work.
However I'm now discovering that some functionality like removeField() isn't working in the connected subcomponents -- e.g. child_form.removeField(index) removes all the child_forms, not just the one matching the index.
What is the correct/best-practice method of connecting child components of a reduxForm() component to the redux state? Passing everything through the hierarchy using props is going to become unwieldy very quickly...
However I'm now discovering that some functionality like removeField() isn't working in the connected subcomponents -- e.g. child_form.removeField(index) removes all the child_forms, not just the one matching the index.
There is two way to solve this:
Use action creators
The removeField method wraps the removeArrayValue action creator. You can dispatch this action to remove an entry of your form.
// Removes the 4th row
dispatch(removeArrayValue("myform", "myarrayfield", 3))
Use a plugin
Within the reducer plugin, you can intercept actions and trigger modifications of your form state. If you dispatch an action, you can intercept it and updates the field array.
Passing everything through the hierarchy using props is going to become unwieldy very quickly...
I've seen several workaround for this.
Create multiple forms using the same form name. Internally Redux Form will merge them so your store contains the data of every sub form within the same object.
Connect child form using connect and use action creators to manipulate the state.
Break your form into smaller parts and pass fields. This is my favourite solution. We successfully do this in our application (on more than a hundred forms!).
const MainForm = reduxForm({
form: "main",
fields: [
SubForm1.fields,
SubForm2.fields,
SubForm3.fields,
]
})(
props => (
<form onSubmit={props.handleSubmit}>
<SubForm1 fields={props.fields} />
<SubForm2 fields={props.fields} />
<SubForm3 fields={props.fields} />
<button type="submit">Send</button>
</form>
)
)
const SubForm1 = ({fields}) => (
<div>
<TextField {...fields.foo} />
<TextField {...fields.bar} />
</div>
)
SubForm1.fields = ["foo", "bar"]