I want to create a component with only one event and place it inside an ancestor of any given level (child or grandchild or deeper), and when the event triggers the parent of this ancestor will act (eg. alert("I am "+parent.name+" and one of my ansestors did something")).
Imagin of an old peoples home whos residents refuse to die and have a million ansestors, and the residents inform eachother everytime one of their ansestors has a birthday.
What would be the most elgant way to do this since i understand that the observer pattern is fround upon in react? and could it be done without passing the parent object manually when creating a child?
One way I know how to do this is to pass a function down the "family tree" that does something in the parent
class Parent extends React.Component {
constructor(props) {
super(props)
this.doSomethingInParent = this.doSomethingInParent.bind(this);
}
doSomethingInParent() {
console.log('running in parent');
}
render() {
return <Child parentsFunction={this.doSomethingInParent} />
}
}
Now if you ran parentsFunction within the child component you would see the console.log inside doSomethingInParent. If you wanted to go deeper you would have to continually pass that function down through props which can get tedious in more complex applications. Which is why redux/flux is popular because it lets you manage state/dispatch actions and avoid long chains of callback functions.
Solution was found using getChildContext , childContextTypes after discussion in :
check if a component is a child or ancestor of another component ReactJS
see example (for 2 solutions) : https://jsfiddle.net/shaibam/d0q5mwc4/33/.
contextTypes: {
notifyChange: React.PropTypes.func
}.....
Related
i tried to update the sound array which i imported from other component every time it is changed. But however, it only fire componentDidMount() only once and it won't run again. Down below is my code on the problem:
//sound array from another component
import { soundArray } from "./CreateRecord";
export default class RecordingList extends React.Component {
constructor() {
super();
this.currentSoundArray = [];
}
componentDidMount() {
console.log(this.currentSoundArray);
this.getCurrentArray();
}
getCurrentArray() {
this.currentSoundArray = soundArray;
}
render(){
...
}
currently when i view the component, the componentDidMound will run once and console the sound array. At first, the sound array is empty:
[]
However, after i put value in the sound array and comeback to view the component, it wont print the console and it won't update the value of this.currentSoundArray
My expected result should be the currentSoundArray will be changed and print to the console every time the soundArray has been changed in another component. for example:
[]
[1,2]
[1,2,4]
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree). Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
It runs only once.
What you are trying to do is access currentSoundArray value when it is updated from another component, now that you will not be able to do traditionally.
Also componentDidMount fires only once when the component is first initialized and rendered.
Solution 1
A better way of doing this is using something like React Redux to manage your application state, this way you would be able to access the states from any component throughout your application.
I have just finished setting up a boiler plate template for this exact thing, if you would like to check it out its on Github :)
Solution 2
If you are not interested in Redux then i would suggest you use react context for this, it will solve your issue as well. you can check out many examples online for example this uses context to share a snackbar across components.
Hope this Helps!
I'll start by saying I'm very new to React and am just playing around with having components interact with each other... trying to get a sense for where state belongs and the most efficient way(s) to render changes on screen.
I have 2 sibling components, Bro and Sis that are direct children of Dad. Bro makes an HTTP request in componentWillMount to get initial values for its state. It then passes one of the pieces of data from the response (uid) back up to Dad (via a method defined in Dad) which is then passed down to Sis via props. Sis then uses this value in making ITS initial HTTP request (in componentDidUpdate) to populate ITS state.
Dad
class Dad extends Component {
state = {
uid: null
}
updateUID = id => {
this.setState({uid: id});
}
}
render() {
return (
<>
<Bro />
<Sis update={this.updateUID} />
</>
);
}
Sis
class Sis extends Component {
state = {
uid: null,
something: null,
another: null
}
componentDidUpdate() {
axios.get('example.com/endpoint2.json')
.then(res => {
/*
transform as needed and put the vales from
res.data into this.state accordingly...
*/
});
}
render () {
return <section>Component: Sis</section>;
}
}
Bro
class Bro extends Component {
state = {
uid: null,
blah: null,
blah-blah: null
}
componentWillUpdate() {
axios.get('example.com/endpoint1.json')
.then(res => {
/*
...
transform as needed and put the vales from
res.data into this.state accordingly...
*/
// pass uid back up to Dad to be passed down to Sis
this.props.update(res.data.uid);
});
}
render () {
return <section>Component: Bro</section>;
}
}
Is this Bro --> Dad --> Sis passing of data the right way to do this? This seems a bit slow and perhaps unnecessarily complicated to me... I think. The alternate ways i can think of doing it are:
have Sis make its initial HTTP request in componentWillMount and fetch the value of uid on its own. This would eliminate the need to pass it from one child to the parent to the other child, but it would involve a partially redundant query on the backend which is why I chose not to go this route.
have Dad make an HTTP request that performs 1 combined query to return the data needed by both Bro and Sis and pass it down to each accordingly. As it stands right now, Dad does not always display Bro and Sis (depending on the route). In those cases, it would be a useless HTTP request and thus definitely not right, but I'm thinking a bit of restructuring may make this viable...
perhaps nesting Dad in something like Grandpa and letting Grandpa take care of the routing while Dad fetches the data for Bro and Sis.
So I guess ultimately my question is: should I be passing data between child/adjacent/sibling components via their parent component or should the parent component be the source of the data for both children and pass it down to each accordingly?
First of all, you shouldn't be calling an HTTP request in componentWillMount(). Instead do so in componentDidMount() as stated in React docs
Your method is complete fine. However based on the container/presentational (smart/dump) components strategy you'd better do all your data fetching in <Dad /> component, then pass down the required data to the children. This way it would be so much easier to keep track of your requests and your data won't be scattered about.
An alternative is to use 3rd-party libraries such as Redux or Mobx State Tree. I'm not sure about Mobx, but what Redux does is it keeps the state outside of the components and make it available to the whole application by React context. You should be thinking about using this as it's extremely powerful and easy to learn
Last but no least, I will include a couple of posts here about container/presentational components pattern:
From Dan Abramov - The creator of Redux
Another medium post
I'm new to react an got a question about components.
I made a component like this:
class HeaderLabel extends React.Component {
constructor() {
super();
}
componentWillMount() {
this.setState({ value: String(this.props.value) });
}
render() {
return (
<div>
{this.props.value && this.props.value != "" ? <label className="control-label">{this.props.value}</label> : <label className="text-muted">({Translation.getPhrase("EMPTY")})</label>}
</div>
);
}
}
HeaderLabel.propTypes = {
value: PropTypes.string
}
export default HeaderLabel;
As you can see I'm using this.props.value to display the value. Is this the right way or is it better to use this.state.value?
Short answer: Use this.props.value.
Since you are taking the value directly from the props without mutating it somehow, there is no benefit at all in first storing it in the state and then using this.state.value.
Basically this boils down to the fundamental idea of React, which is that you should only ever have "one source of truth". By having value both in your props and in the state you have two sources of truth. As a developer, you should try to have a "master state" which passes the data down to its components.
Sometimes, however rarely, it can be a good idea to store the prop value directly to your state. For example, you might want to have a parent component which holds a string which is displayed in your app. This string can be modified in a modal which is opened when clicking the appropriate button. The "temporary under-edit" string would be in the modal-state, whereas the previously saved one still in the master component.
You might find this official blog article helpful: You Probably Don't Need Derived State
In this example, you are using the props value to display the data which you have received, then using this.props.value makes sense. Props are read-only, means the child is not the owner of that particular data and hence it cannot change anything on that data.
when to use this.state.value, let say a scenario where you get data from parent and you have to do some manipulations on that data before displaying, then, in that case, take props into a state and do the manipulations.
you can use both this.props.value and this.state.value. But first you must answer the following question. Depending on the answer you choose appropriate way of setting the value.
Which component handles the subsequent change of value received from this.props.value:
Change of value is coming from Parent component.Then you should use this.props.value.There is no need for you to assign this.props.value to this.state.value. Because the state is maintained inside parent component.
2.Change is handled by HeaderLabel component itself. Then you should set this.state.value.
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.
The standard way for a React component to include child components is to create them in the render method and set to the children property. In my use case, children may be created before the parent is rendered and passed in through the parent's properties.
Events in the child are bubbled up to the parent as expected, but changes to the parent container do not re-render children created this way. The docs indicate that there is a difference between parent and owner relationships, the latter being established only for components created in render, so my guess is this relationship is missing and important for cascading re-renders.
Here is a simple example (fiddle)
/** #jsx React.DOM */
globalState = 'initial state';
var Child = React.createClass({
render: function() {
return React.DOM.input({
value:globalState
});
}
});
var Parent = React.createClass({
handleChange: function(e) {
globalState = e.target.value;
this.forceUpdate();
},
render: function() {
return React.DOM.div({
children: [
Child(),
React.DOM.br(),
this.props.passedChild
],
onChange: this.handleChange
});
}
});
c = Child();
p = Parent({passedChild:c});
React.renderComponent(p, document.body);
In this example, both child inputs can be edited, the onChange event is caught by the parent and a forceUpdate() is called. This does cascade down to the first child which is created in the render method, but not to the second child which is created elsewhere and passed in.
How can I update the owner of a child component so it will update as desired?
My backup plan is to wire up an event listener on child components. In my application, there is quite a bit of logic around when components are created that would make it impractical to do everything in render().
This is slightly complex to explain here, but your code will now work on the master build of React (post 0.10.0).
I'm not too sure what you're trying to accomplish here, but if you change your this.props.passedChild to this.props.passedChild() and c = Child(); to c = Child;, it'll work. Call it this.props.passedChildClass or something. You can also try this.props.passedChildFn with c = function() { return Child(); }. Whatever suits your need.
Don't create an instance of a component and pass it around (it won't be a big problem anymore soon; the return value of Child() won't be an instance anymore). In general it's bad practice because this encourages mutation. Create your children on the fly as you need them and let React handle the rest.
In my application, there is quite a bit of logic around when components are created that would make it impractical to do everything in render().
Break them into helper functions! You really don't have to stuff everything into a single render.
Also, global state is a bad idea. The fact that your child updates correctly by reading from globalState is very fragile. You must have gotten a warning in your console (providing you're using the dev build) to add an onChange handler. Go read it =).