How to transfer states among components? - javascript

I'm new to React JS, I want to create a ToDo list app. I have App.js which is the main page and so I have created MainBlock component for App.js page which holds left and right side of my layout and in right side property it loads Form component which has input and button and saves input values to an array in state and in left side it loads List component which has a property named allTasks which prints all tasks.
My problem is how can I transfer allTasks state from Form Component to App.js to pass it to List component property?
In App.js :
<MainBlock
left={
<List allTasks={['خرید از فروشگاه', 'تعویض دستگیره درب منزل']} />}
right={
<Form />
} />

You can accomplish this by storing the tasks as state in the App component, and have Form pass up the state through a callback prop. This concept is called "lifting state up". Here's a guide about it: https://reactjs.org/docs/lifting-state-up.html
<MainBlock
left={
<List allTasks={this.state.allTasks} />
}
right={
<Form onSubmit={allTasks => this.setState({ allTasks })} />
}
/>

Let tasks be a state of the MainBlock component. Pass this state down to the List component.
Let the Form component expose a callback property (maybe onTaskCreated) which is invoked when user complete creating a new task.
Let MainBlock intercept onTaskCreated callbacks and update it's internal state with the new task, which causes it to re-render and thus passing down the new task list to the List component.

You are trying to achieve two way binding here. Unfortunately, React is a library that doesn't support two way binding by default.
You can only pass in the values from Parent to Child to Sub-Child. However, the reverse is not true.
In this case, you are trying to load props from Form to App that is from Child to Parent which is not possible.
You should make use of state containers such as Redux that overcomes this limitation by making the state of one component available to the other component.

You can store all your tasks in the state of the MainBlock component and pass functions to update the state by your form. Also, pass the state (tasks) of MainBlock to your List component. Updating your tasks to MainBlock will automatically pass the tasks to List. Later you could use a state management library like MobX and Redux.

It can be confusing passing data between components. Libraries like redux were created to make this "easier", but it can be done with simple React. To pass state information to children, you pass it into their props. To send data the other direction, you pass (from parent to child) a handler function that is called from the child and can affect the state of the parent (or be passed to it's parent) as needed.
I wrote a sample pen to help people wrap their heads around this. Hopefully it helps.
Something like:
<Child1 handleAdd={this.handleAdd.bind(this)}
handleSubtract={this.handleSubtract.bind(this)}
/>
calls in the parent and and is accessed in the child as:
<Button onClick={this.onAdd.bind(this)}>
add
</Button>
<Button onClick={this.props.handleSubtract.bind(this)}>
subtract
</Button>
Then there is a function in the parent called handleAdd that can affect the parent state.

Related

Pass data from child component to another child

I'm using React JS
I want to pass data from child component to another child component.
-App
-- Home
--- Filter ( Here is I need to to pass data from Filter component To Card component it's useState value)
--- Card ( Here I need to resive data )
Thre are two ways to solve this problem:
1 (I'd recommend for a smaller project): Just keep the state that needs to be shared in the parent component and pass it to the childs as a prop. You can update the state by creating a callback function in the parent component and calling it from the cild (after passing it as a prop aswell)
2: Use a state management libary like react-redux

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) }/>

Is there a way to call a child-component's function from another child?

I'm contributing to an open-sourced React/Redux application built using ES6 JavaScript. I'm fairly new to React/Redux so I'm having some trouble.
I have a parent class that's rendering two different React components. The first component contains some input fields regarding events (called NewShift). The second component is a calendar that renders these events (called Index).
Once a user fills out the input fields and presses a button in the first component, I want to be able to re-render the calendar in the second component. If the re-render function is in the calendar component, how do I call it from the input fields component (both children).
React re-renders components whenever component state is changed. Should you be using just React, this would mean passing changed values up to the parent component's state to force a re-render.
However, Redux makes this easier on you, as it's 'Store' functions as a global state. You can force a re-render by changing appropriate variables within the store.
Given your situation: the button should get this within it's onClick attribute:
onClick={() => dispatchNewCalendarInfo(payload)}.
dispatchNewCalenderInfo should also be imported by the component:
import { dispatchNewCalendarInfo } from './redux/path/to/post/actions.js'; and connected to it: export default connect(()=>({}), { dispatchNewCalendarInfo })(Component);. Note, you need to also import connect from 'react-redux' for this.
And, of course, dispatchNewCalendarInfo should be present in the actions.js path, and accepted by the store reducer. This dispatch should alter information that the calendar is connected to, which will force it to update and re-paint.
If you're not using Redux there's another path you can take. Instead of having the function that takes input be in NewShift, have new shift receive the function as a prop from the parent.
So in your NewShift component you would have something like onClick={this.props.submitCalanderInfo()}
The submitCalanderInfo function would be part of the parent component. You would probably want this new info to be saved into the state of the parent component, and then then use that state to update the props on the calendar or Index component. So Index might look something like this:
<Index shiftData={this.state.shiftData} />

Vue.js component within component, manipulate parent data

ive seen afew answers that sort of answer my question but not fully, so let me explain what I want to do.
We use a global #app div within the layout of our website, which is a Laravel project. So all pages will be the same main Vue instance, due to this i'm separating key functionality into components.
So, the first example is just a simple Tab component, this either separates any children into tabs, or accepts some data which the single child component then renders.
So below i'm injecting some data from another component, this ajax component literally just does an ajax call, and makes the data available within it's slot.
<ajax endpoint="/api/website/{{ $website->id }}/locations/{{ $location->slug }}/get-addresses">
<div>
<tabs :injected="data">
<div>
<div v-for="row in data">
#{{ row['example' }}
</div>
</div>
</tabs>
</div>
</ajax>
Now this is all well and good, to a point, but this falls down with the below code. This contains a component which will allow the used to drag and drop elements, it re-arranges them by literally moving the data around and letting Vue handle the DOM changes.
This will of course work fine within it's own data which you have injected in, but when you change the data within the component below this then clears this child component.
<ajax endpoint="/api/website/{{ $website->id }}/locations/{{ $location->slug }}/get-addresses">
<div>
<tabs :injected="data">
<div>
<div v-for="row in data">
<draggable :injected="row">
<div>
<div v-for="item">
#{{ item }}
</div>
</div>
</draggable>
</div>
</div>
</tabs>
</div>
</ajax>
I need to find a way to make any changes to this data apply to the parent data, rather than the data passed into the child components.
What is the best practice to do this!?
Edit 1
Basically, I need any child component's manipulate the data within the ajax component. The children within ajax could change, or there could be more, so I just need them all to do this without knowing what order or where they are.
It is hard to come up with specifics on this one, but I am going to try to put you in the right direction. There are three ways to share data between components.
1) Passing down data via props, emitting data up via custom events
The passing down of data via props is a one-way street between the parent and child components. Rerendering the parent component will also re-render the child and data will be reset to the original state. See VueJS: Change data within child component and update parent's data.
2) Using a global event-bus
Here you create an event bus and use this to emit the data to different components. All components can subscribe to updates from the event bus and update their local state accordingly. You initiate an event bus like this:
import Vue from 'vue';
export const EventBus = new Vue();
You send events like this:
import { EventBus } from './eventbus.js'
EventBus.$emit('myAwsomeEvent', payload)
And you subscribe to events like this:
import { EventBus } from './eventbus.js'
EventBus.$on('myAwsomeEvent', () => {
console.log('event received)
})
You still need to manage state in the components individually. This is a good start with an Event bus: https://alligator.io/vuejs/global-event-bus/
3) Using Vuex
Using Vuex extracts the component state into the Vuex store. Here you can store global state and mutate this state by committing mutations. You can even do this asynchonously by using actions. I think this is what you need, because your global state is external to any components you might use.
export const state = () => ({
resultOfAjaxCall: {}
})
export const mutations = {
updateAjax (state, payload) {
state.resultOfAjaxCall = payload
}
}
export const actions= {
callAjax ({commit}) {
const ajax = awaitAjax
commit('updateAjax', ajax)
}
}
Using vuex you keep your ajax results separated from your components structure. You can then populate your state with the ajax results and mutate the state from your individual components. This way, it doesn't matter whether you recall ajax, or destroy components since the state will always be there. I think this is what you need. More info on Vuex here: https://vuex.vuejs.org/

Why use props in react if you could always use state data?

I understand that there's two ways to pass components data: props and state. But why would one need a prop over a state? It seems like the state object could just be used inside the component, so why pass the prop parameters in markup?
Props are set externally by a parent component. E.g.;
render() {
return <ChildComponent someProp={someValue}/>;
}
State is set internally, and often triggered by an user event within a child. E.g.;
handleUserClickedButton: () {
this.setState({
buttonClicked: true
});
},
render() {
return <button onClick={this.handleUserClickedButton}/>;
}
So, props are a way for data to go from parent to child. State is a way for data to be managed within a singular component, and possibly have changes to that data triggered by children. In effect, they represent data traveling in 2 opposite directions, and the way in which they are passed is entirely unique.
There are two ways to "pass" or access data from outside your component but state is not one of them.
The two ways are:
Props - which a parent component pass down to the child component.
Context - which you can "skip" the direct parent in the tree.
The state is an internal object which no other component has access to it unless you pass it explicitly (via the two ways mentioned above).
So basically your question is not accurate as you can't really compare the two.
I think what you are really asking is why using a state-less instead of a state-full component.
Which you can find an answer here in Stack-overflow or in other websites.
Edit
A followup to some of your comments.
why does the child not just have a shared state? for example, each
component (or sub-component) could just do a "this.state" to get the
current state of the program
The same way you can't share or access private objects in other
functions.
This is by design, you share things explicitly and you will pass
only what the component needs. For example, look it this page of
stack-overflow, lets say the voting buttons are components, why
would i pass them the whole state if it only needs the vote count
and 2 onClick event listeners? Should i pass the current logged in
user or maybe the entire answers rendered in this page?
so you can't pass state between a parent to child? for example, can't
the parent change the state and then the child gets the new state
This is exactly what the props or context should do, provide an API for sharing data between parents and children though we keep it in a one way data flow, from parents to children, you can't pass props upwards. but you invoke handlers passed down to your child components and pass data through that handler.

Categories

Resources