I'm just wondering if there's anyway of completely cloning an already rendered React component. I've read about cloneElement but when I render that clone I get an error:
Uncaught error: Invariant Violation: Element type is invalid
I'm also wondering if this will clone the element's child elements, and their subsequent input values. For example, if some text is in one of the inputs, and then the component is cloned, will these values be preserved? Or is my only option to store those values before clone? This will make it very tightly coupled I feel.
As a general rule, we should always try to keep our components as stateless as possible, meaning that the data you input in the form should be stored somewhere outside of the component (a Store, maybe? ... I'm thinking about http://alt.js.org/docs/stores/).
With this approach, you'll have your component listening to that store. You can have as many copies of the component as you'd like, but the single source of truth would be your store.
Another scenario, if the same component should show data from different stores, then do not use stores, and use props. Have the parent component be the one listening to its store, and pass the necessary data to the child component (the one that you want to have clones in several places in your app).
I hope that helps,
Related
Whenever I use Frameworks such as React , Vue or anything else. I see that every value (input value, select value ...) is always being emitted through events to parent. I don't really get why this is good. It takes more ressources / time to pass it to the parent component. Why doesn't the component handle its data by itself?? Why should every single value inside a child component get passed to the state of the parent to make the handling of the data ?
For example, I generally see the loading of the APIs in the parent and then the values of the API get passed down through props or Redux/Vuex. Why can't we get the API values inside the child directly when it's being mounted instead of having to pass it down on many levels or through the local store
It's perfectly valid to have a state inside reusable component, especially if it holds users input. You can still provide it with callbacks in props, which respond to onChange, onBlur or other events based on your needs, or you can communicate with your reusable component via refs, which is fairly flexible way to get user input.
The important thing is, that the reusable component should be as much independent on it's environment as possible. If you don't provide it with a state, the component (in case of your Pickers) will be dependent on props passed down from its parent expressing it's current user input on every rerender and on callbacks updating its current input somewhere higher in component hierarchy on every change, which is not what you want in every case, and it already forces you to make relatively large adjustments in you parent component, which misses the point of reusability.
I have one component, it contains two other components. First "NotifyMessage" component is rendered for the whole page. Second "NotifyMessage" component is rendered just only inside pop up. Both components subscribe to the redux store and get appropriate message and type (success or error) from there. Currently, if something happens - "NotifyMessage" component rendered in both places (popup and whole page). What is the best approach to separate render logic? I'd like to render only one component in one place.
create a flag State , say "compAlreadyShown" with boolean value. Use it to conditionally show hide in popup.
I've added another message to my redux store for "pop'ups" cases and pass it to my common "NotifyMessage" component as a children. For now I've two sources of truth for messages in my store instead of one. May be there is a better solution but it fix my problem.
I'm building an application where I would like to provide separate views for same data.
In my current implementation, data is obtained by web service call and persisted in state of App component in App.js. App component hosts (renders) another component called StackEditor, which acts as a view for this.state.components in App component.
UI elements rendered by StackEditor can be moved around, and to synchronize state of App I do it as below:
<StackEditor
components={this.state.components}
onLocationChanged={this.handleLocationChanged} />
In handleLocationChanged I update the state:
handleLocationChanged(e, data) {
this.setState(prevState => {
// event data copied to state here
return {components: prevState.components};
});
}
As state is now updated, this forces StackEditor to be rendered again, as its property components is bound to state as components={this.state.components} (see in the code snippet above).
This all works, but now I started questioning if I'm doing it right.
Q1: Should I be using state instead of props?
It seems that location of component is mutated in principle, although from StackEditor point of view, it is immutable (I can decide that change is invalid and not to update the state in event listener).
Q2: Is it possible to share part of the state between 2 components in React?
If I somehow convert StackEditor from getting components from state instead of props, will I get notification on state changed by child component (StackEditor) in my parent component (App)?
Q3: Also, are props more convenient to use than state in general?
When I created another component following HOC guidelines (https://reactjs.org/docs/higher-order-components.html) I discovered that props are easily forwarded to "wrapped" component, but not state. If I provide a function to call back via property (as I did above), "wrapped" component can easily call it, without even noticing that it's "wrapped". I don't see how I can easily notify "wrapped" component about state change in "wrapper", without writing some extra code.
If you imagine your application to be a tree of components in a well designed app it's usually like this:
the leafs are stateless components . They decide how data is rendered.
the nodes are stateful components. They decide which components and data to render.
Q1: Should I be using state instead of props?
It depends on which category of components you have (node or leaf).
Q2: Is it possible to share part of the state between 2 components in
React?
If you feel that your app has a lot of state that mutates and needs to be used by several components spread over your tree you usually start to introduce an external state management library (e.g. redux). Components can subscribe to your store and become stateless as your store now handles the state.
Q3: Also, are props more convenient to use than state in general?
They solve different problems so you can't really say that. A stateless component is usually easier to understand but has no capabilities to control anything.
Also read Identify where your state should live and When to use redux.
All that is only a rule of thumb. A lot of the time you will have components that have both state and props because they control parts of your app but delegate other parts to their children.
This all works, but now I started questioning if I'm doing it right.
As far as I can see from the code you provided this looks pretty much as it has to.
In the docs, React says that it doesn't really care for instances as the Components take props as input and outputs elements for you. It gives you an example at the top of how other frameworks have to create an instance and then connect it to the DOM to handle different events. But I don't understand how this is different than what React is doing.
You're not calling new on your component in React, but you still have to render it and create all the same handlers. And this inside the component still refers to the instance, so doesn't React still have to create an instance each time your component is rendered (even if it's a component inside an <li> that's being rendered several times at once).
Traditional frameworks will have to create multiple instances of the same component to connect to each DOM node it corresponds to, isn't that what React is doing too? How else can one component keep track of multiple this's?
Indeed, React creates Component instance internally. You don't need to worry about using new.
React Element is just a plain JavaScript Object that describes what you want to be rendered (React.Component or HTML Element, if type is a String).
From the docs:
An element is not an actual instance. Rather, it is a way to tell
React what you want to see on the screen. You can’t call any methods
on the element. It’s just an immutable description object with two
fields: type: (string | ReactClass) and props: Object1.
The difference is that you the developer are not having to write the code to do all that. You just write the render method and your callbacks and let React worry about creating the DOM elements and the component instances and connecting them together.
I have a UI that is basically a form, but it has different components for editing specific pieces of data. I want all of the data on the page to be saved at the same time, when the user clicks the save button. All the the data is contained by in a type, and I have an update mutation for that type. So, while the different children components may be editing different pieces of the object that will be saved, the parent component is going to be responsible for performing a single update mutation that saves all the data.
The problem I am running into is that I am not sure where I should have the state for the child components. No matter what, it seems that the state for them will have to be in the parent so that it has it and can include it in the input object that it passes to the mutation when it performs it. However, these leaves me in one of two places, neither which seems ideal:
Only store the state of the values being edited by the child components in the parent.
Issue: Relay throws a warning, because I am passing a state object to a child RelayContainer, when it expects it to be a fragment fetched by Relay. You can see this warning and an example of this behavior in this playground. Here is the specific warning I am referring to that you see when editing a field in a child component.
Warning: RelayContainer: Expected prop details supplied to [object Object] to be data fetched by Relay. This is likely an error unless you are purposely passing in mock data that conforms to the shape of this component's fragment.
Let the child components maintain their own state, but then also communicate the values to the parent so that it has them for the purposes of handling the save action and performing the mutation.
Issue: Duplication of state. Even though the state that is in the parent is solely being kept for the purposes of performing the mutation, it seems quite wrong to have it there and will cause unnecessary renders for the parent. You can see an example of this variation in this other playground.
Hopefully there is an obvious preferred way to handle this situation, but from my searching around and playing with Relay I have not been able to determine it.
First things first. Very well-written question. Thank you :-)
The common pattern used in React and Relay world is that we have a parent component which acts a thin view controller, fetches data using Relay and passes its state's different parts as props to different child component as necessary. The child components, which receive data as props, usually are dumb and just display passed data.
In your case, the ChildComponent itself edits data. It does receive initial details data from its parent component HelloApp. However, when we edit any of the details fields in UI, the values fetched by Relay and those in UI do not match. While creating a Relay container for child component, if we include fragments and fields, we declare that those data will be fetched by Relay. Now, if we want to modify child components' data(props) ourselves, we just skip that fragment in the Relay container. The props passed by the parent component will be used only for initialState of the child component.
The necessary changes are in HelloApp container. The fields of details are moved from child to parent.
HelloApp = Relay.createContainer(HelloApp, {
fragments: {
person: () => Relay.QL`
fragment on Person {
name,
details {
hairColor,
nickName
}
}
`,
}
});
BUT I still feel uncomfortable with the above solution. Because it clearly shows that, the parent has to know the fields of details, which are actually used in child components.