Unable to bind handler in React - javascript

I am trying to bind a method of a parent component to the state of its child component but I'm unable to get the desired result. I checked the value of 'this' in App component and it still points to the App component. Should it not be pointing to the ItemsList component since its being binded to it using bind()? Can someone please point out the mistake I'm making.
import React from 'react';
import {render} from 'react-dom';
class Item extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> {this.props.value} </div>;
}
}
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
itemArray: ['Work', 'Learn React']
}
this.props.adder.bind(this);
console.log(this.props.adder)
}
render() {
const items = this.state.itemArray.map(el=><Item key={el} value={el} />);
return (
<div>
<h2> To Do List </h2>
<ul>{items}</ul>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
}
addElement (data) {
let items = this.state.ItemList;
items.push(<Item value={data} />);
}
render() {
return (
<div>
<input type="text" ref={input=>this.input=input} />
<input type="button" value="Add" onClick={()=>this.addElement(this.input.value)}/>
<ItemList adder={this.addElement} />
</div>
);
}
}
render(<App />, document.getElementById('root'));

Should it not be pointing to the ItemsList component since its being binded to it using bind()?
Well,the step you following in not right one.
In App Component
You need to store the ItemList (child) component reference in App(parent) component.
<ItemList adder={this.addElement} bindChild = {(ref)=>this.itemList = ref}/>
In ItemList component,
you need to call bindChild method when ItemList component mounted.
componentDidMount(){
this.props.bindChild(this);
}
Now, in your App (parent) component, you have reference for ItemList (child) component in this.itemList property.
In App component, you can use this.itemList to update state of ItemList (child) component.
addElement(data) {
let items = this.itemList.state.itemArray;
console.log(items);
const newItem = <Item value={data} />
this.itemList.setState({ itemArray : [...items, newItem]})
}
Please check complete example on codesandbox

Though what you want is technically possible, this is a much more explicit easy to understand way to do it.
I re-factored your code so that the data flow only goes in one direction, from App to `Itemimport React from "react";
import { render } from "react-dom";
I also changed Item and ItemList to stateless components that take value and items as props respectively.
The main change is that App holds the state instead of ItemList
const Item = ({ value }) => <div>{value}</div>;
const ItemList = ({ items }) => (
<div>
<h2>To Do List</h2>
{items.map(item => <Item key={item} value={item} />)}
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: ["Work", "Learn React"]
};
}
addElement(value) {
this.setState(state => ({
items: [...state.items, value]
}));
}
render() {
return (
<div>
<input type="text" ref={input => (this.input = input)} />
<input
type="button"
value="Add"
onClick={() => this.addElement(this.input.value)}
/>
<ItemList items={this.state.items} />
</div>
);
}
}
render(<App />, document.querySelector("#root"));
Here is a CodeSandbox with your working app: https://codesandbox.io/s/4r4v0w5o94

Related

to display a different component with each click (using hooks)

I want to display a different component with each button click.
I'm sure the syntax is wrong, can anyone help me? The browser doesn't load
I would love an explanation of where I went wrong
One component (instead of HomePage) should display on the App component after clicking the button. Help me to understand the right method.
Thanks!
App.js
import React, {useState} from 'react';
import './App.css';
import Addroom from './components/Addroom.js'
import HomePage from './components/HomePage.js'
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button onClick={()=>{setFlage({flag:true})}}>Addroom</button>
<button onClick={()=>{setFlage({flag:false})}}>HomePage</button>
{setState({flag}) && (
<div><Addroom index={i}/></div>
)}
{!setState({flag}) && (
<div><HomePage index={i}/></div>
)}
</div>
)
}
export default App;
HomePage
import React from 'react'
export default function HomePage() {
return (
<div>
HomePage
</div>
)
}
Addroom
import React from 'react'
export default function Addroom() {
return (
<div>
Addroom
</div>
)
}
I didn't test it but as i can see it should be something like this:
<button onClick={()=>setFlage(true)}>Addroom</button>
<button onClick={()=>setFlage(false)}>HomePage</button>
{flag && (
<div><Addroom index={i}/></div>
)}
{!flag && (
<div><HomePage index={i}/></div>
)}
You need to call setFlage function with argument of Boolean saying true or false and it changes the flag variable that you want to read.
Try the following.
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button
onClick={() => {
setFlage(true);
}}
>
Addroom
</button>
<button
onClick={() => {
setFlage(false );
}}
>
HomePage
</button>
{flag ? <Addroom /> : <HomePage /> }
</div>
);
}
You are missing render methods and also you should use setState for reactive rendering.( when you use state variables and once value changed render method will rebuild output so this will load your conditinal component.
https://jsfiddle.net/khajaamin/f8hL3ugx/21/
--- HTML
class Home extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Home</div>;
}
}
class Contact extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Contact</div>;
}
}
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
flag: false,
};
}
handleClick() {
this.setState((state) => ({
flag: !state.flag,
}));
console.log("hi", this.state.flag);
}
getSelectedComp() {
if (this.state.flag) {
return <Home></Home>;
}
return <Contact></Contact>;
}
render() {
console.log("refreshed");
return (
<div>
<h1>
Click On button to see Home component loading and reclick to load back
Contact component
</h1
<button onClick={() => this.handleClick()}>Switch Component</button>
{this.getSelectedComp()}
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"));

Passing a variable to `renderMenuItemChildren` in react typeahead bootstrap

From the App component it passes the variableitem to the Todo component. From the Todo component passes to theSearchResult component. However, the `SearchResult 'component is not displayed.
Demo here: https://stackblitz.com/edit/react-e2zqvd
import {Typeahead} from 'react-bootstrap-typeahead';
class App extends Component {
constructor() {
super();
this.state = {
name: [{id:1, name: 'mario'}, {id:2, name: 'paul'}],
item: 'flower'
};
}
render() {
return (
<div>
<Todo
item = {this.state.item}
name = {this.state.name}
/>
</div>
);
}
}
class Todo extends Component {
constructor(props){
super(props);
}
render () {
return (
<div>
<Typeahead
id={'example4'}
labelKey= 'name'
multiple
options={this.props.name}
onChange={this.handleSelectPeopleToCalendar}
ref={(ref) => this._typeahead = ref}
renderMenuItemChildren={(option, props) => (
<SearchResult
key={option.id}
user={option}
item={props.item}
/>
)}
/>
</div>
)
}
}
const SearchResult = ({user, item}) => (
<div>
<p>{item}</p>
<span>{user.name}</span>
</div>
);
try
<SearchResult
key={option.id}
user={option}
item={this.props.item}
/>
and you need start type something to see this component, there have input with no borders, and without background

How to test ForwardRwf components with jest/enzyme?

I have such a component:
import React, { Component } from 'react';
export class TopicsList extends Component {
constructor(props) {
super(props);
this.state = {
topics: [...],
};
this.references = [];
}
getOrCreateRef(id) {
if (!this.references.hasOwnProperty(id)) {
this.references[id] = React.createRef();
}
return this.references[id];
}
render() {
const {
topics
} = this.state;
return (
<div>
<ul>
{topics.map((topic) => (
<TopicItem
key={topic.id}
topic={topic}
ref={this.getOrCreateRef(topic.id)}
/>
)
)}
</ul>
</div>
)
}
}
const TopicItem = React.forwardRef((props, ref) => {
return (
<li
>
<p>{props.name}</p>
<i
className="fal fa-plus"
/>
</li>
);
});
I wrote test to test how much li items will be rendered:
test('should render 3 li items', () => {
console.log(wrapper.debug())
expect(wrapper.find('TopicItem').length).toBe(3);
});
but my test failed because in jest they recognized like:
<ul>
<ForwardRef topic={{...}} />
<ForwardRef topic={{...}} />
<ForwardRef topic={{...}} />
</ul>
How can I test components that are returned with React.forwardRef?
I cannot find appropriate solutions on the internet or here.
It is a bit late, but assigning the displayName property to the wrapped component can help. Enzyme respects displayName and uses it when creating snapshots (rendering it instead of ForwardRef in this case), and find also works with display names.

Share state between childs in react with objects

I'm having some problems when I try to update all childs states from one of the child, here is an example of my code. The idea is to autoupdate all components from one of them.
I'm new in react, I have only been using for a week, so probably all this is a misunderstanding.
https://codesandbox.io/s/430qwoo94
import React from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
filedStr: 'some text',
fieldObj: {
field1: true,
field2: true
}
}
}
updObj = (which, val) => {
this.setState(prevState => ({
fieldObj: {
...prevState.fieldObj,
[which]: val,
},
}));
};
render() {
return (
<div>
<h2>Parent</h2>
Value in Parent Component State: {this.state.fieldObj.field1 ? 1 : 0} : {this.state.fieldObj.field2 ? 1 : 0}
<br />
<Child obj={this.state.fieldObj} onUpdate={this.updObj} />
<br />
<Child obj={this.state.fieldObj} onUpdate={this.updObj} />
<br />
<Child obj={this.state.fieldObj} onUpdate={this.updObj} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
obj: props.obj
}
}
update = (which) => {
this.props.onUpdate(which, !this.state.obj[which]);
this.setState(prevState => ({
obj: {
...prevState.obj,
[which]: !prevState.obj[which],
},
}));
};
render() {
return (
<div>
<h4>Child</h4>
Value in Child State: {this.state.obj.field1 ? 1 : 0} : {this.state.obj.field2 ? 1 : 0}<br />
<button type="button" onClick={(e) => { this.update('field1') }}>field1</button>
<button type="button" onClick={(e) => { this.update('field2') }}>field2</button>
</div>
)
}
}
render(<Parent />, document.getElementById('root'));
When all child components values are directly derivable from the props you do not need to create a state in child which is a replica of props and maintain it, what you need to do is modify the parent's state directly like
import React from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
filedStr: 'some text',
fieldObj: {
field1: true,
field2: true
}
}
}
updObj = (which, val) => {
this.setState(prevState => ({
fieldObj: {
...prevState.fieldObj,
[which]: val,
},
}));
};
render() {
return (
<div>
<h2>Parent</h2>
Value in Parent Component State: {this.state.fieldObj.field1 ? 1 : 0} : {this.state.fieldObj.field2 ? 1 : 0}
<br />
<Child obj={this.state.fieldObj} onUpdate={this.updObj} />
<br />
<Child obj={this.state.fieldObj} onUpdate={this.updObj} />
<br />
<Child obj={this.state.fieldObj} onUpdate={this.updObj} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
obj: props.obj
}
}
update = (which) => {
this.props.onUpdate(which, !this.props.obj[which]);
};
render() {
return (
<div>
<h4>Child</h4>
Value in Child State: {this.props.obj.field1 ? 1 : 0} : {this.props.obj.field2 ? 1 : 0}<br />
<button type="button" onClick={(e) => { this.update('field1') }}>field1</button>
<button type="button" onClick={(e) => { this.update('field2') }}>field2</button>
</div>
)
}
}
render(<Parent />, document.getElementById('root'));
CodeSandbox
However if you want to why your way of handling doesn't work as expected it, is because, you are not updating the state of the child components based on the state update in the parent, you were only setting it once in the constructor which is only called once when the component mounts, what you need is to implement the componentWillReceiveProps lifecycle function
Here, I've updated your code to meet your need -https://codesandbox.io/s/llnzm2y95z
Your assumption of child re-rendering is wrong. When the child rerenders the constructor method is not called in other words, constructor is only called once. To use next props and change in states you need to make use of the renders and componentWillReceiveProps. See react-component lifecycle http://busypeoples.github.io/post/react-component-lifecycle/
The problem is when you updated the parent's state using onClick={(e) => { this.update('field1') }} and onClick={(e) => { this.update('field1') }}
You updated the parent's state and this state was again passed to the child. But in the child you are not using this new props. You' re instead using the state, this state was updated only in the constructor, which is not updated after the new props got received. (As the constructor gets called only once)
One way to handle the new props is directly using the props in the render as the component will rerender and the updated props will be available to it.
The other way if you want to make use of the state, then update the state inside componentWillReceiveProps. (I would also want to point out that it is highly not-recommended to do setState inside a componentWillReceiveProps and componentDidMount). So better use the first step.
componentWillReceiveProps(newProps) {
if(newProps !== this.props){
this.setState({newStateObjects})
}
}

How to update the props of a rendered react component from App.js?

I have a React component MoviesGallery.js with the following configuration:
class MoviesGallery extends Component {
constructor(props) {
super(props)
this.state = { currentImage: 0 };
this.closeLightbox = this.closeLightbox.bind(this);
this.openLightbox = this.openLightbox.bind(this);
this.gotoNext = this.gotoNext.bind(this);
this.gotoPrevious = this.gotoPrevious.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({movies_genre: nextProps.movies_genre})
}
I have rendered the component in my main App.js file like so:
class App extends Component {
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons"/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery/>
</div>
</MuiThemeProvider>
);
}
}
I want to update the props of my MoviesGallery component without recreating the component. Since I already added the componentWillReceiveProps() to MoviesGallery component, how can I make it so when 'Query' button is clicked, it will pass new props to the already rendered MoviesGallery and componentWillReceiveProps() should cause it to re-render since the state will change.
Just confused about the function that will change the props themselves on-click of the rendered MoviesGallery component.
Thanks in advance!
When a parent pass a new (value) prop to the child, the child component will call the render method automatically. There is no need to set a local state inside the child component to "store" the new prop.
Here is a small example of a Counter that receives a count prop and just displays it, while the parent App in this case will change the value in its state and pass the new value to Counter:
class Counter extends React.Component {
render() {
const { count } = this.props;
return (
<div>{count}</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
onClick = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
const { count } = this.state;
return (
<div>
<Counter count={count} />
<button onClick={this.onClick}>Add to counter</button>
</div>);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
you can use the 'state' for your MovieGallery.js props because the state is an object that changes and you must your code like below :
class App extends Component {
state = {
query : null
}
myFunction(query){
this.setState({query});
}
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons" onClick={this.myFunction = this.myfunction.bind(this)}/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery newProps = {this.state.query}/>
</div>
</MuiThemeProvider>
);
}
}
i hope it helps

Categories