Calling child method from Parent in Reactjs - reactjs

I know it's a question asked many times on SO but I am still facing a problem don't know how to resolve.
import Events from './subComponents/Events'
export default class userHome extends Component {
constructor() {
super();
this.state = {
events:[],
};
this.changeView=React.createRef();
}
changeViewClick = () =>{
this.changeView.current.changeDataView();
};
render() {
const {events} = this.state
return (
<IconButton onClick={this.changeViewClick}>
<CardView />
</IconButton >
<IconButton onClick={this.changeViewClick}>
<TableView />
</IconButton>
<Events ref={this.changeView} events={events} />
);
}
}
Events Component
export default class Events extends Component {
constructor(props) {
super(props);
}
changeDataView = () => {
console.log("hi");
}
render() {
return (<div>Hey Child</div>);
}
}
I am getting error as
TypeError: _this.changeView.current.changeDataView is not a function
My reactjs version is 16.6.3

In my opinion, have you tried to pass an anonymous function to child component ?
onClick={() => this.yourfunction()}
It happened, I think, because you called in your child component this props : this.changeView.current.changeDataView()
So when you pass that props in the child component you must passed it as an anonymous function to tell React it's a function to execute when the onClick event is triggered.
Let me know if that resolve your problem or if I am wrong

Related

Create a removable React component without having to re-create remove function

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?

React componentDidUpdate() does not fire

I have an react app with primereact installed and I am using primereact/captcha.
Maybe I have misunderstood something, but isn't the following code supposed to work (console.log('Child component did update'))?
import React from 'react';
import { Captcha } from 'primereact/captcha';
export default function App() {
return (
<div className="App">
<ParentComponent/>
</div>
);
}
class Child extends React.Component {
componentDidUpdate () {
console.log('Child component did update');
}
render() {
return (<h2>Child component</h2>);
}
}
class ParentComponent extends React.Component {
constructor() {
super();
this.state = {
captchaSovled: false,
key : Math.random()
}
}
render() {
let output;
if (this.state.captchaSolved) {
output = <Child key={this.state.key} />;
} else {
output =<Captcha siteKey="xxxxxxx" onResponse={() => this.setState({ key : Math.random(), captchaSolved: true })} />
}
return (
<div>
<h1>Parent component</h1>
{output}
</div>
);
}
}
From React doc
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
In your code, the Child component is mounted after captchaSolved state is set, therefore only componentDidMount is fired on Child component.
componentDidUpdate is fired, if there is any change in the state or props. As of your component child:
class Child extends React.Component {
componentDidUpdate () {
console.log('Child component did update');
}
render() {
return (<h2>Child component</h2>);
}
}
There is no state or props which are changing, that's why componentDidUpdate never get's invoked.

React component not updating from props

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

Sharing store change event between same hierarchical level child components

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.

Export functions out of react native components

I just want to export a function to an actions.js file but i cannot get to work.
This is the working base:
export default class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
visible: false
}
}
onOpen = () => {
this.setState({visible:true});
console.log(this.state.visible);
}
render() {
return (
<View style={styles.container}>
<Button onPress={()=>{this.onOpen();}}>More</Button>
</View>
);
}
}
And now i tried this, which gives me an error when i press the button:
Error:
Unhandled JS Exception: _this.setState is not a function. (In '_this.setState({ visible: true })', '_this.setState' is undefined)
Code:
let onOpen = () => {
this.setState({visible:true});
console.log(this.state.visible);
}
export default class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
visible: false
}
this.onOpen = onOpen.bind(this);
}
render() {
return (
<View style={styles.container}>
<Button onPress={()=>{this.onOpen();}}>More</Button>
</View>
);
}
}
You cannot use the 'this' keyword outside the class Component. you cannot change the state of the component from outside the class.
More details here:
Change state Outside Component
Further, If you want to change the state of the component outside a class, make use of redux state.
Redux JS
The problem with your code is that you've define onOpen outside your class and you want to access setState function of the Component. I don't know why one would want to do this because onOpen belongs to class. But still if you want to put this outside class. You can do this by following way:
let onOpen = ({setState}) => {
//retrieve setState here
setState({visible:true});
}
export default class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
visible: false
}
// You don't have to do this if onOpen is not in your class
//this.onOpen = onOpen.bind(this);
}
render() {
//send this.setState to onOpen
const that = this;
return (
<View style={styles.container}>
<Button onPress={()=>{onOpen({setState: that.setState});}}>More</Button>
</View>
);
}
}

Resources