I am looking to create a "delete-able" / removable React component that I can use in multiple different places.
From researching, I can see it is kind of an anti-pattern to create a component that deletes itself and the correct way to do things is for the parent to manipulate the child components rather than child components modifying themselves.
This has led me to write code somewhat along the following lines:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: [ XXX ]
};
}
removeFunc = (index) => {
const test = this.state.data.filter((_,i) => i !== index);
this.setState({data: test});
}
render() {
return (
<div>
{this.state.data.map((el,i) =>
<ChildComponent removeFunc={() => this.removeFunc(i)}/>
)
}
</div>
);
}
}
export default ParentComponent;
class ChildComponent extends Component {
constructor(props) {
super(props);
this.state = {
removeFunc: props.removeFunc
};
}
render() {
return (
<div>
<button onClick={this.state.removeFunc}>Delete Me</button>
</div>
);
}
}
export default ChildComponent;
The issue I have with this is that I have to keep re-writing the removeFunc function in every parent component.
I am VERY new to React, so I'm just curious if there is there a better / different way to do this or is this the correct way?
Related
I want to know if there is possible to get the component instance as I need.
I put my new component as a children in the main state, but is no the same object in both files.
I need to reach children state in my MainComponent. Looking in google for componenet instance doesnt help, maybe I am out of focus and the name of this is different.
Here is my MainComponent:
import React, { Component } from 'react';
import AnotherComponent from './../whatever/AnotherComponent';
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
children: [],
};
}
addChild() {
const { children } = this.state;
this.setState({
children: children.push(<AnotherComponent />)
});
}
getChildrenState(component) {
return component.state(); // this doesn't work!
}
render() {
const { children } = this.state;
return (
<div>
{(children.map(i => (<div key={this.getChildrenState(i).id}>{i}</div>))}
</div>
)
}
And This is AnotherComponent
import React, { Component } from 'react';
class AnotherComponent extends Component {
constructor(props) {
super(props);
this.state = {
id: 144,
};
}
render() {
return (
<div>
Here it is my cHild!
</div>
)
}
Putting <AnotherComponent/> to the state doesn't make sense because it's React element object that isn't associated with specific component instance.
Accessing children state in parent component breaks the encapsulation and indicates design problem.
The instance of class component should be retrieved with a ref, and doing so to access instance state is the last resort that may be needed to extend third-party components that don't provide desired functionality.
If AnotherComponent is first-party component, it should be designed accordingly, to not require state to be accessed from the outside:
render() {
return (
<div key={this.state.id}>{this.state.id}</div>
)
}
If the output needs to be more flexible, it can make use of render prop pattern:
render() {
const render = React.Children.only(children);
return (
<div key={this.state.id}>{render(this.state.id)}</div>
)
}
And used like:
<AnotherComponent>{id => <div>{id}</div>}</AnotherComponent>
If you want to access the state of the child component ( here AnotherComponent ) then you can either :
Maintain the state inside the AnotherComponent and pass the value to the parent ( here MainComponent ) on a change listener ( whenever the state changes ), or;
Maintain the state in the parent ( here MainComponent ) and pass the value to the child as prop.
Let me know if you want me to give an example implementation.
While this question has been asked before I did not find an answer. I have components nested to the level of great grandchild and I don't know how to get the data from the bottom to the top.
<Parent/>
<Child/>
<GrandChild/>
<GreatGrandChild/>
See an example: fiddle
The great grandchild is a form and I want the input data to get to the parent at the top. I had it working when it was just nested one level deep, but now that it is deeply nested it does not work. I'm not sure how to even pass the event up two levels.
I've heard using redux is possible but I wonder if there is a way to avoid it. Or, how do I avoid the nesting? Even through they are all actually separate components should I just move them into one big component? This might work but seems like bad practice?
Very simplified, you could just pass the function through all the components:
class GreatGrandChild extends React.Component {
render() {
return (
<div>
<input onChange={this.props.onChange}/>
<h2>I'm the GreatGrandChild</h2>
</div>
)
}
}
class GrandChild extends React.Component {
render() {
return (
<div>
<h2>I'm the GrandChild</h2>
<GreatGrandChild onChange={this.props.onChange}/>
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild onChange={this.props.onChange}/>
<h2>I'm the child</h2>
</div>
)
}
}
class Top extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
handleChildchange = (e) => {
console.log('child event on parent')
console.log(e.target.value);
}
render() {
return (
<div>
<Child onChange={this.handleChildchange}/>
<h2>I'm the parent</h2>
</div>
)
}
}
ReactDOM.render(<Top />, document.querySelector("#app"))
Redux is overkill for simple passing of props. You can pass props down through each child but it's easier to use the Context API like so:
Parent Component:
const MyContext = React.createContext('default');
export MyContext;
class Parent extends React.Component {
myFunction() {
//Do something here
}
render() {
return (
<MyContext.Provider value={this.myFunction}>
<ChildComponent />
</MyContext.Provider>
);
}
}
export default Parent;
Child Component:
import { MyContext } from './Parent';
class ChildComponent extends React.Component {
render() {
const { myFunction } = this.context;
return (
<div onClick={myFunction}>Click Me!</div>
);
}
}
ChildComponent.contextType = MyContext;
You can use the context as deep as you'd like, as long as you import it.
Simply pass a callback down from the parent via the props and make Sure it's passed all the way down to where you need it.
You also can pass props to your each child component in nesting and whenever values changed, you can call a parent function (nested) to get latest values in parent.
I have simplified the code to isolate and reproduce the issue, so it may not make sense in real implementations:
import React, { Component } from 'react'
const obj = {
objProp: true
};
export default class MyButtonContainer extends Component {
render() {
return (
<MyButton
onClick={() => {obj.objProp = !obj.objProp;}}
text={obj.objProp.toString()}
/>
);
}
}
class MyButton extends Component {
render() {
return (
<button
onClick={this.props.onClick}
>
{this.props.text}
</button>
)
}
}
You can see that obj.objProp is assigned into MyButton.props.text, and it's value gets toggled when you click on an instance of MyButton. The value of obj.objProp does change as expected, but MyButton doesn't update and rerender.
My question is why is MyButton is not updating, and what is the proper way to implement such logic?
In addition, if the solution is to push obj into MyButtonContainer.state, why MyButton would of update if I have used Redux, which injects data only into props without changing the state?
Thanks :)
What you need is a state, You should not use variable this way, it needs to be on state and changing that state asynchronously.
Change your button container to this.
export default class MyButtonContainer extends Component {
constructor() {
this.state = {
objProp: true
}
this.onclick = this.onclick.bind(this);
}
onclick() {
this.setState({ objProp: !this.state.objProp })
}
render() {
return (
<MyButton
onClick={() => { this.onclick() }}
text={this.state.objProp.toString()}
/>
);
}
}
Demo
Use state to hold your objProp
React will rerender when there is setstate is called, it won't get rerendered automatically.
export default class MyButtonContainer extends Component {
state = {
objProp: true
}
onclick = () => {
this.setState({ objProp: !this.state.objProp })
}
render() {
return (
<MyButton
onClick={() => { this.onclick() }}
text={this.state.objProp.toString()}
/>
);
}
}
}
Whenever there is something where you want the UI to change it should be either through its State or by props passed to it.
Both the given answers are right, if you want to re-render your component you must use this.setState. so there is two way to get your updated data in React Component.
1) put your object in to state and setState.
2) if you really dont want to use your object in state, you can do a workaround like take a variable i in your state and when assigning the data in your object just do this.setState({i+1}), so due to change in state will re-render your component although this is not good way to resolve it, because to re-render you must setState.
import React, { Component } from 'react'
constructor(props){
super(props);
this.state = {i:0}
}
const obj = {
objProp: true
};
export default class MyButtonContainer extends Component {
render() {
return (
<MyButton
onClick={() => {
obj.objProp = !obj.objProp ;
let {i} = this.state ;
i = i + 1;
this.setState(i)}}
text={obj.objProp.toString()}
/>
);
}
}
class MyButton extends Component {
render() {
return (
<button
onClick={this.props.onClick}
>
{this.props.text}
</button>
)
}
}
I am developing a simple React JS application for learning purpose. I just started learning React JS a few days ago. Now, I am having a problem with Flux Store. I need to share the change event across two child components on the same hierarchical level.
I have the parent component, called TodoComponent with the following definition
//Create
class TodoComponent extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<div>
<ListComponent />
</div>
<AddItemComponent />
</div>
)
}
}
It has two child components called, ListComponent and the AddItemComponent. Moreover, I have a store with this definition.
import { EventEmitter } from 'events';
class DataStore extends EventEmitter{
constructor()
{
super();
this.todos = [
"Eat",
"Sleep",
"Die",
"Shower"
];
}
getAll(){
return this.todos;
}
addItem(newItem)
{
this.todos.push(newItem);
this.emit("change")
}
}
const dataStore = new DataStore;
export default dataStore;
It has a function for adding new item into the array and a function for fetching the array.
This is the ListComponent that is displaying the array of items from the DataStore flux store.
import React from 'react';
import TodoItem from './TodoItem';
import DataStore from './data.store';
class ListComponent extends React.Component{
constructor(props)
{
super(props)
this.state = { todos : DataStore.getAll() };
}
componentWillMount(){
DataStore.on('change', () => {
//do somethif
this.state = { todos : DataStore.getAll() };
})
}
render()
{
var deleteItem = (item) => {
this.deleteItem(item);
}
var editItem = (item) => {
this.editItem(item);
}
var addItem = (newItem) => {
this.addItem(newItem);
}
var todos = this.state.todos.map((item, index) => {
return (
<TodoItem item={item} addItem={addItem.bind(this)} deleteItem={deleteItem} editItem={editItem} />
)
});
return (
<ul>
{todos}
</ul>
)
}
deleteItem(item)
{
this.setState({ todos: this.state.todos.filter((listItem, index) => {
return listItem !== item;
}) });
}
editItem(item)
{
alert(item)
}
addItem(newItem)
{
DataStore.addItem(newItem);
}
}
module.exports = ListComponent;
It is updating the items in the change event of the DataStore store. But I am not calling the addItem function in the ListComponent. I am calling it in the AddItemComponent.
This is the definition of the AddItemComponent.
import React from 'react';
import DataStore from './data.store';
class AddItemComponent extends React.Component{
constructor(props)
{
super(props)
}
render()
{
return (
<form id="form-todo" onSubmit={this.addItem.bind(this)} action="post">
<input type='text' ref="newItem" />
<button>ADD</button>
</form>
);
}
addItem(e)
{
e.preventDefault();
DataStore.addItem(this.refs.newItem.value);
}
}
module.exports = AddItemComponent;
But when I trigger the addItem function in the AddItemComponent, the change event of the DataStore in the ListComponent is not triggered. Therefore, how can I synchronize the change event of Flux Store between two components exist on the same hierarchical level?
The solution I can think of is having the DataStore in the TodoComponent (parent component) and send the data and functions as props to the child component. I think, the code will become a bit messy in that way. Is that the only solution to do that?
Welcome to React! I recreated your example and your "change" event is firing in ListComponent, but to update the state in a component you should use this.setState(changes) rather than this.state = {changes}. Only use this.state = {} in the constructor to set the initial state. The setState method properly flows through the React lifecycle and causes the component to re-render using the new state. There is an official guide on React's state and lifecycle hooks here.
I have two components. I want to call a method of the first component from the second component. How can I do it?
Here is my code.
First Component
class Header extends React.Component{
constructor(){
super();
}
checkClick(e, notyId){
alert(notyId);
}
}
export default Header;
Second Component
class PopupOver extends React.Component{
constructor(){
super();
// here i need to call Header class function check click....
// How to call Header.checkClick() from this class
}
render(){
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default PopupOver;
You can do something like this
import React from 'react';
class Header extends React.Component {
constructor() {
super();
}
checkClick(e, notyId) {
alert(notyId);
}
render() {
return (
<PopupOver func ={this.checkClick } />
)
}
};
class PopupOver extends React.Component {
constructor(props) {
super(props);
this.props.func(this, 1234);
}
render() {
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default Header;
Using statics
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
render: function() {
}
});
MyComponent.customMethod('bar'); // true
Well, actually, React is not suitable for calling child methods from the parent. Some frameworks, like Cycle.js, allow easily access data both from parent and child, and react to it.
Also, there is a good chance you don't really need it. Consider calling it into existing component, it is much more independent solution. But sometimes you still need it, and then you have few choices:
Pass method down, if it is a child (the easiest one, and it is one of the passed properties)
add events library; in React ecosystem Flux approach is the most known, with Redux library. You separate all events into separated state and actions, and dispatch them from components
if you need to use function from the child in a parent component, you can wrap in a third component, and clone parent with augmented props.
UPD: if you need to share some functionality which doesn't involve any state (like static functions in OOP), then there is no need to contain it inside components. Just declare it separately and invoke when need:
let counter = 0;
function handleInstantiate() {
counter++;
}
constructor(props) {
super(props);
handleInstantiate();
}
You could do this to call a method of the child component from the parent component.
import React from 'react';
class Header extends React.Component {
constructor() {
super();
this.childComponentRef;
}
getChildComponent = (childComponent) => {
this.childComponentRef = childComponent;
this.childComponentRef.sayHi();
}
render() {
return (
<ChildComponent getChildComponent={this.getChildComponent} />
)
}
};
class ChildComponent extends React.Component {
componentDidMount () {
this.props.getChildComponent(this);
}
sayHi = () => {
alert("hi");
}
render() {
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default Header;