I'm trying to pass data through 3 components within the hierarchy (Parent-Child-Child2):
Parent-class:
class Parent extends React.Component {
constructor(props) {
super(props);
this.handleOperation = this.handleOperation.bind(this);
}
handleOperation = (value) => {
// some actions
};
render() {
return (
<div className={styles}>
<Child valueChild={this.handleOperation()}/>
</div>
);
}
}
Child1:
class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange =(value) => {
this.props.valueChild(value);
};
render() {
return (
<div>
<Child2 childValue2 = {this.handleChange} />
</div>
);
}
}
Child2:
class Child2 extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange =(event) => {
this.props.childValue2(event.target.value);
};
render() {
return (
<div>
<input type="button" onClick={this.handleChange} value="defaultValue" />
</div>
);
}
}
Code compiling successfully, but when I click on input-field (to pass value to Parent), I receive the following error:
Related
I am new to React and I am finding it difficult to pass props from one component to another.
This is the first component
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
// show:true
};
}
counter = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div className="">
<div>{this.state.count}</div>
</div>
);
}
}
and this is the second
export default class Button extends React.Component {
render() {
return (
<div className="">
<App />
<button onClick={this.counter}>Click me</button>
</div>
);
}
}
How do I make the counter count by passing props in the apps component
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
// show:true
};
}
counter = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div className="">
<div>{this.state.count}</div>
<Button counter={this.counter} />
</div>
);
}
}
export default class Button extends React.Component {
render() {
return (
<div className="">
<button onClick={this.props.counter}>Click me</button>
</div>
);
}
}
I have two React Components, Gallery & Image. The Image Component uses a Gallery function as props.
Can I do the call in the render without an arrow function?
Image component:
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
filter: 'none',
};
}
render() {
return (
<div>
<button className="image-icon" onClick={() => this.props.handleClone(this.props.i)} />
</div>
);
}
}
Gallery component:
class Gallery extends React.Component {
constructor(props) {
super(props);
this.handleClone = this.handleClone.bind(this);
this.state = {
images: [],
};
}
handleClone(i) {
var newImages = this.state.images;
newImages = newImages.slice(0, i + 1).concat(newImages.slice(i));
this.setState({
images: newImages,
});
}
render() {
return (
<div>
<div className="gallery-root">
{this.state.images.map((dto, i) => {
return <Image key={'image-' + dto.id + '-' + i} i={i} handleClone={this.handleClone} />;
})}
</div>
</div>
);
}
}
Thanks.
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
filter: 'none'
};
}
handleClick = () => {
this.props.handleClone(this.props.i);
};
render() {
return (
<div>
<button className="image-icon" onClick={this.handleClick} />
</div>
);
}
}
I want to make a component with an API like any standard input element, meaning I want to use it like this: <CustomInput value={this.state.custom_input_state} onChange={this.handleChange} />
Here is what I have so far, but I have no idea how to
Make the custom components value changeable from the parent component
after it has been constructed
Make the parent's onChange handler function recieve a change event when the
custom component's value changes
Here is my test setup:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
foo: 0
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.increment = this.increment.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert(this.state.foo);
event.preventDefault();
}
increment() {
this.setState({foo: this.state.foo + 1});
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<div onClick={this.increment}>Increment from parent</div>
<CustomInput name="foo" value={this.state.foo} onChange={this.handleChange}/>
<input type="submit" value="Submit" />
</form>
)
}
}
class CustomInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value,
};
this.increment = this.increment.bind(this);
}
increment() {
this.setState({value: this.state.value + 1});
}
render() {
return(
<React.Fragment>
<div onClick={this.increment}>Increment self</div>
<input name={this.props.name} value={this.state.value}/>
</React.Fragment>
);
}
}
You have to pass all the CustomInput props to the input element. In CustomInput component actually it not recieving the onChange event.
Pass the prop onChange event to input element
Form Component
class Form extends Component {
constructor() {
super();
this.state = {
foo: 'React'
};
}
handleChange = (event) => {
this.setState({
[event.target.name]:event.target.value
})
}
render() {
return (
<div>
<form>
<Custominput name="foo" onChange={this.handleChange} value={this.state.foo} />
</form>
{this.state.foo}
</div>
);
}
}
CustomInput Component
export default class CustomInput extends React.Component{
render(){
return(
<input {...this.props} />
)
}
}
demo link
I have a constructor in my main component:
class App extends Component {
constructor(props){
super(props);
this.state = {
items: []
}
};
render() {
return (
<div className="App">
<ItemList items={this.state.items}/>
<AddItemForm items={this.state.items}/>
</div>
);
}
}
In component AddItemForm I'm adding to array items objects with properties "item_name" that is string and "comment" with data type object. View of component:
class AddItemForm extends React.Component {
constructor(props) {
super(props);
this.state = {
item:{}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({item:
{
item_name: event.target.value,
comment:{}
}
});
}
handleSubmit(event) {
event.preventDefault();
this.props.items.push(this.state.item);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" item_name={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default AddItemForm;
How can I iterate this array to get all item_name values of every object and display them as list in my ItemList component?
This should help.
class App extends Component {
constructor(props){
super(props);
this.state = {
items: []
}
};
addItemToItemsList = (item) => {
const {items=[]} = this.state;
items.push(item);
this.setState({
items : items
});
}
render() {
return (
<div className="App">
<ItemList items={this.state.items}/>
<AddItemForm
items={this.state.items}
addItemToItemsList={this.addItemToItemsList}
/>
</div>
);
}
}
class ItemList extends React.Component {
render () {
const {items} = this.props;
return (
<div>
{items.map((item, index) => {
return (
<div key={index}>item.item_name</div>
)
})}
</div>
);
}
}
class AddItemForm extends React.Component {
constructor(props) {
super(props);
this.state = {
item: {
item_name : '',
comment:{}
}
};
}
handleChange = (event) => {
const new_item = Object.assign({}, this.state.item, {item_name: event.target.value});
this.setState({
item: new_item
});
}
handleSubmit = (event) => {
event.preventDefault();
this.props.addItemToItemsList(this.state.item);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" item_name={this.state.item.item_name} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default AddItemForm;
I think, you have an error inside AddItemForm, you should pass onSubmit function from App to AddItemForm and change items through this function:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
}
this.handleSubmit = this.handleSubmit.bind(this);
};
handleSubmit(value){
this.setState({
items: this.state.items.concat(value)
})
}
render() {
return (
<div className="App">
<ItemList items={this.state.items} />
<AddItemForm
onSubmit={this.handleSubmit}
items={this.state.items} />
</div>
);
}
}
About main question, one of the way to solve this problem
const ItemList = ({items}) => (
<div>
{items.map( (item, index)=> (
<div key={index}>{item.item_name}</div>
))}
</div>
);
full working example here: https://codesandbox.io/s/7k624nz94q
You can't add to the array directly. You need to pass a callback that will add to the array in your parent component's state. This is a very common pattern when using react.
Here is a skeleton of what you need to do:
In your parent component, you don't need to pass the whole list to your AddItemForm component, just a addItem callback to your child component:
class App extends Component {
constructor(props){
super(props);
this.state = {
items: []
}
this.addItemToList = this.addItemToList.bind(this);
};
render() {
return (
<div className="App">
<ItemList items={this.state.items}/>
<AddItemForm addItemToList={this.addItemToList}/>
</div>
);
}
addItemToList(newValue) {
// Here you add the item to your state
// Always treat your state as immutable, so create a copy then add the item, then set your new State
const newArray = this.state.items.slice(); // clone
newArray .push(newValue); // Add value
this.setState({items: newArray}); // Set the new state
}
}
More info on how to add items to an array in the state here: React.js - What is the best way to add a value to an array in state
Then you use that callback in your child component:
handleSubmit(event) {
event.preventDefault();
// this.props.items.push(this.state.item);
// Here don't mutate the props, instead call the callback to add the item to your parent's component's state
this.props.addItemToList(this.state.item);
}
To display a list of items, you need to use the map function: https://reactjs.org/docs/lists-and-keys.html#rendering-multiple-components
I have a problem (
There are my components:
class Main extends Component {
constructor(props) {
super(props);
this.state = {
total: 0
};
this.totalFuns = this.totalFuns.bind(this);
}
totalFuns(event){
this.setState({total: event})
}
render() {
return (
<main>
<Item data_items={data} data_cnt={this.totalFuns} />
</main>
);
}
}
export default Main;
then Item component:
class Item extends Component {
constructor(props){
super(props);
this.skuChange = this.skuChange.bind(this);
}
skuChange(event) {
this.props.data_cnt(event)
}
render() {
return (
<section className="item" data-index={this.props.data_index}>
<Select values={this.props.data_items} onChange={this.skuChange}/>
</section>
);
}
}
export default Item;
then Select component
class Select extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.props.onChange(event.target.value);
this.setState({value: event.target.label});
}
render() {
var options = this.props.values;
var options_list = options.map(function(obj,i){
return (
<option value={i} key={i} label={obj.label} />
)
});
return (
<select value={this.state.value} onChange={this.handleChange}>
{options_list}
</select>
);
}
}
export default Select;
In console show: Uncaught TypeError: Cannot read property 'totalFuns' of undefined. Without this fun my app work correct.. (
Why?
Your function is actually called totalFuns but you have totalFun.
So this line:
<Item data_items={data} data_cnt={this.totalFun} />
should be
<Item data_items={data} data_cnt={this.totalFuns} />
In your Select component, the state value was initially undefined and hence you were getting error. Also, the options can be selected based on value instead of label. See the working snippet below. Also I do not see data being present in the Main component
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
total: 0
};
this.totalFuns = this.totalFuns.bind(this);
}
totalFuns(event){
console.log(event);
this.setState({total: event})
}
render() {
var data = [{label: 1}, {label: 2}]
return (
<main>
<Item data_items={data} data_cnt={this.totalFuns} />
</main>
);
}
}
class Item extends React.Component {
constructor(props){
super(props);
this.skuChange = this.skuChange.bind(this);
}
skuChange(event) {
this.props.data_cnt(event)
}
render() {
return (
<section className="item" data-index={this.props.data_index}>
<Select values={this.props.data_items} onChange={this.skuChange}/>
</section>
);
}
}
class Select extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.props.onChange(event.target.value);
this.setState({value: event.target.value});
}
render() {
var options = this.props.values;
var options_list = options.map(function(obj,i){
return (
<option value={obj.label} key={i} label={obj.label} />
)
});
return (
<select value={this.state.value} onChange={this.handleChange}>
{options_list}
</select>
);
}
}
ReactDOM.render(<Main/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>