How to Stop So Many Bubble Ups of Events in Reactjs? - reactjs

due to certain factors(SalesForce) I am looking of only using reactjs and not something like redux.
I going to have something like this
<script type="text/babel">
class Main extends React.Component {
constructor() {
super();
this.state = {
items : [
{
year: 2016,
},
{
year: '',
},
]
};
}
componentDidMount() {
}
handleChange(event, index){
let items = this.state.items;
let item = items[index];
item[event.target.name] = event.target.value;
items[index] = item;
this.setState({ items: items});
}
render() {
return (
<div className="main">
{
this.state.items.map((item, i) => {
return <Item item={item} index={i} handleChange={(event,index) => this.handleChange(event,index)} />
})
}
</div>
)
}
}
class Item extends React.Component {
constructor() {
super();
}
componentDidMount() {
}
handleChange(event){
this.props.handleChange(event, this.props.index);
}
render() {
return (
<div className="item">
<Sub item={this.props.item} index={this.props.index} handleChange={(event) => this.handleChange(event)} />
</div>
)
}
}
class Sub extends React.Component {
constructor() {
super();
}
componentDidMount() {
}
handleChange(event) {
this.props.handleChange(event, this.props.index);
}
render() {
return (
<div className="container">
<div>
<label>Year </label>
<input type="text" name="year" value={asset.year} className={year} onChange={(event) => this.handleChange(event)}/>
</div>
</div>
)
}
}
ReactDOM.render(<Main />, document.getElementById("Container"));
</script>
I am wondering is there away so that when someone types something in "year" in sub component I don't have to have handleChange that goes up to the Item handleChange and finally to the Main handleChange.

Related

Cannot change parent's state from child component in React

I'm building a simple chat app, but a new comment posted from the input field in the child component is not displayed.
--Parent component--
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
comments: [],
currentUser: { displayName: "user3", uid: 3 }
};
}
addComment = comment => {
this.setState(prevState => {
comments: prevState.comments.push(comment);
});
console.log("this.state");
console.log(this.state);
};
render() {
const { comments, currentUser } = this.state;
return (
<div className="App">
{comments.map(comment => (
<div className="line__left" key={comment.createdAt}>
<figure>
<i className="fas fa-user fa-4x" />
</figure>
<div className="line__left-text">
<div className="name">{comment.createdBy.displayName}</div>
<div className="text">{comment.text}</div>
</div>
</div>
))}
<ChatInputBox addComment={this.addComment} currentUser={currentUser} />
</div>
);
}
}
--Child component--
class ChatInputBox extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "",
currentUser: this.props.currentUser
};
}
handleChange = e => {
this.setState({ text: e.target.value });
};
handleClickPost = e => {
e.preventDefault();
let comment = {
createdAt: new Date().getTime(),
createdBy: this.state.currentUser,
text: this.state.text
};
this.props.addComment(comment);
this.setState({ text: "" });
};
render() {
const { text } = this.state;
return (
<div className="ChatInputBox">
ChatBox
<textarea onChange={this.handleChange} value={text} />
<button onClick={this.handleClickPost}>Post</button>
</div>
);
}
}
After I populate the text area and click the button, parent's state seems to be updated, but new comment is not shown.How can I show it?
change your code
addComment = comment => {
this.setState(prevState => {
comments: prevState.comments.push(comment);
});
console.log("this.state");
console.log(this.state);
};
to
addComment = comment => {
const { comments } = this.state;
this.setState({
comments: comments.concat(comment)
});
};
when you are using setState(), using concat instead of push since it maintains your array's immutability.

React select values not remaining selected

I am trying to create a select dropdown that onChange updates all the select dropdowns in the table.
State is being set correctly. However when I change any of the selects in the table the value does not remain selected (displayed).
It sets state correctly fro both children and master selects. Its just not displaying the option selected.
import React, { Component } from 'react';
// import ReactDOM from 'react-dom';
// import { Select } from 'element-react';
import Select from 'react-select';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
categories: [],
filterText: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.masterCategoryChange = this.masterCategoryChange.bind(this);
}
handleFilterTextChange(filterText) {
this.props.handleFilterTextChange(filterText)
this.setState({
filterText: filterText
});
}
handleChange(product, index, e){
product.category_id = e.value;
let selectValues = this.state.selectValues;
selectValues[index] = e.value;
this.setState({selectValues});
}
masterCategoryChange(e){
let selectValues = this.state.selectValues;
selectValues.forEach(function(sv, index) {
selectValues[index] = e.value;
});
console.log(selectValues)
this.setState({selectValues});
}
/* Inside your component */
componentDidMount() {
const apiRequest = url => fetch(url).then(response => response.json())
const apiRequestProducts = () => {
return apiRequest("http://docker.for.mac.localhost:4000/api/products?filter[limit]=5").then(function(response) {
return response;
});
};
const apiRequestCategories = () => {
return apiRequest("http://docker.for.mac.localhost:4000/api/categories?filter[limit]=7").then(function(response) {
return response;
});
};
this.setState({loading: true});
Promise.all([
apiRequestProducts(),
apiRequestCategories()
]).then(results => {
var selectValues = [];
for (var i=0; i < results[0].length; i++) {
selectValues[i] = '';
}
this.setState({
selectValues,
products: results[0],
categories: results[1],
loading: false
});
}).catch(err => {
console.log('Oops, something went wrong', err);
});
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<FilterableProductTable
masterCategoryChange={this.masterCategoryChange}
handleChange={this.handleChange}
products={this.state.products}
categories={this.state.categories}
filterText={this.state.fliterText}
categorySelectValues={this.state.selectValues}
/>
</div>
);
}
}
class FilterableProductTable extends React.Component {
constructor(props) {
super(props);
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleCategoryChange = this.handleCategoryChange.bind(this);
this.handleMasterCategoryChange = this.handleMasterCategoryChange.bind(this);
}
handleFilterTextChange(filterText) {
this.props.handleFilterTextChange(filterText)
}
handleCategoryChange(product, index, e){
this.props.handleChange(product, index, e)
}
handleMasterCategoryChange(e){
this.props.masterCategoryChange(e)
}
render() {
return (
<div>
<SearchBar
filterText={this.props.filterText}
onFilterTextChange={this.handleFilterTextChange}
/>
<ProductTable
handleMasterCategoryChange={this.handleMasterCategoryChange}
handleCategoryChange={this.handleCategoryChange}
handleFilterTextChange={this.handleFilterTextChange}
products={this.props.products}
categories={this.props.categories}
filterText={this.props.filterText}
selectValues={this.props.categorySelectValues}
/>
</div>
);
}
}
class ProductTable extends React.Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
this.onProductCategoryChange = this.onProductCategoryChange.bind(this);
this.masterCategoryChange = this.masterCategoryChange.bind(this);
}
handleClick = () => {
console.log('this is:', this);
}
onProductCategoryChange(product, index, e){
this.props.handleCategoryChange(product, index, e)
}
masterCategoryChange(e){
this.props.handleMasterCategoryChange(e)
}
render() {
console.log('Rendering');
console.log(this.props.selectValues);
let options = this.props.categories.map(function (category) {
return { value: category.id, label: category.name };
})
console.log(options);
let rows = this.props.products.map((product, i)=> {
console.log(this.props.selectValues[i])
return (
<tr key={i}>
<td>{product.name}</td>
<td>{product.price}</td>
<td><Select
options={options}
value={this.props.selectValues[i]}
// value={product.category_id}
onChange={this.onProductCategoryChange.bind(this, product, i)}
/></td>
</tr>
)
});
return (
// <h3>MasterCategory : {masterCategoryId}</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th><CategorySelect
onChange={this.masterCategoryChange.bind(this)}
className='masterCategorySelect'
categories={this.props.categories}/></th>
</tr>
</thead>
<tbody>{rows}</tbody>
<tfoot>
<tr>
<td><div className='pull-right'><button onClick={this.handleClick}>Categorize</button></div></td>
</tr>
</tfoot>
</table>
);
}
}
class CategorySelect extends React.Component {
render() {
let options = this.props.categories.map(function (category) {
return { value: category.id, label: category.name };
})
return (
<Select
onChange={this.props.onChange}
options={options}
// value={value}
/>
);
}
}
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
}
handleFilterTextChange(e) {
this.props.onFilterTextChange(e.target.value);
}
render() {
return (
<form>
<input
type="text"
placeholder="Search..."
value={this.props.filterText}
onChange={this.handleFilterTextChange}
/>
</form>
);
}
}
export default App;

React.js Bind results as cards which are getting from web api

Hi all I have a working sample when I use the data as constant in React js. The same output I need when the data is returning from a controller can some one help me. Here is the expected on https://codepen.io/anon/pen/KQVmzK
My code with controller call, in render I would like to render the result as in the sample
class UserList extends React.Component {
constructor() {
super();
this.state = { person: [] };
}
componentDidMount() {
this.UserList();
}
UserList() {
fetch("/ReactJS/GetMessage")
.then(res => res.json())
.then(
(result) => {
alert(result);
this.setState({
//isLoaded: true,
person: result
});
}
)
}
render() {
// need to bind the html result as per in the fiddle
}
}
ReactDOM.render(<UserList />, document.getElementById('form'));
render(){
{this.state.person.length > 0
?
<CardLists cards={this.state.person}>
:
null
}
}
<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>
Finally after lot of research I did myself
class Application extends React.Component {
constructor() {
super();
this.state = {
camperArr: []
};
}
componentDidMount() {
this.callApi("/ReactJS/GetMessage");
}
callApi(str) {
var req = str;
this.serverRequest = $.get(req, function (result) {
result.map(function (elem, idx) {
elem["id"] = idx + 1;
});
this.setState({
camperArr: result
});
}.bind(this));
}
render() {
return (
<CardList list={this.state.camperArr} />
);
}
}
// the list of campers
class CardList extends React.Component {
render() {
var list = this.props.list;
return (
<div>
{list.map((card, index) => <Card key={index} {...card} />)}
</div>
);
}
}
class Card extends React.Component {
render() {
return (
<div style={{ margin: 'lem' }}>
<img width="75" src={this.props.UserImage} />
<div style={{ display: 'inline-block', marginLeft: 10 }}>
<div style={{ fontSize: '1.25em', fontWeight: 'bold' }} >{this.props.UserName}</div>
<div>{this.props.Company}</div>
</div>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('form'));

React Passing state to sibling component and up to parent class

Very very new to React and I seem to be stuck. This is a simple Todo app, I basically have 3 components, the base component, an input component and a task component. I have figured out how to edit the state within each component but I am having trouble passing state from component to component.
class App extends Component {
render() {
return (
<div id="appContainer">
<HeaderTitle />
<TaskInput />
<Task taskState={true} text="task one" />
<Task taskState={true} text="task two" />
<Task taskState={true} text="task three" />
</div>
);
}
}
class TaskInput extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
update(e) {
this.setState({inputValue: e.target.value});
console.log(this.state);
}
taskCreate(e) {
this.setState({text: this.state.inputValue, completeState: false});
console.log('button clicked');
console.log(this.state);
}
render () {
return (
<div className="taskInputContainer">
<TaskInputField update={this.update.bind(this)} taskCreate={this.taskCreate.bind(this)} />
</div>
)
}
}
class Task extends Component {
constructor(props) {
super();
this.state = {
completeState: false
}
}
toggleTask (e) {
this.setState({
completeState: !this.state.completeState
});
}
delete (item) {
}
render() {
return (
<div className="taskContainer" onClick={this.toggleTask.bind(this)}>
<div className={"taskState " + this.state.completeState}></div>
<div className={"taskText " + this.state.completeState }>{this.props.text}</div>
<div className="taskDelete"><i className="fa fa-times-circle-o" aria-hidden="true"></i></div>
</div>
);
}
}
const TaskInputField = (props) =>
<div className="taskInputContainer">
<input type="text" className="taskInputField" onChange={props.update}/>
<i className="fa fa-plus-circle" aria-hidden="true" onClick={props.taskCreate}></i>
</div>;
Task.propTypes = {
text: PropTypes.string.isRequired,
completeState: PropTypes.bool
};
Task.defaultProps = {
text: 'Task',
completeState: false
};
const HeaderTitle = () => (
<h1>Davids Todo List</h1>
);
export default App;
So in the TaskInput has its own state that I can update but how do I pass that up to the parent component to update and add a Task component? Also how do I add a Task component without re-rendering the whole thing?
This issue is documented in detail in the article 'lifting the state up' in React's documentation.
TLDR, you create a handler that updates the state of the current component and pass it to children as props. In the example below (a modified version of your code), I passed down the methods that changes the state of component App, into its children components (TaskInput and Tasks).
class App extends React.Component {
constructor() {
super();
this.state = {
tasks: [],
}
}
addTask = (e, text) => {
e.preventDefault();
const newTask = {
id: new Date().getTime(),
done: false,
text
};
const newTasks = this.state.tasks.concat([newTask]);
this.setState({
tasks: newTasks
})
}
toggleTask = (id) => {
const updatedTask = this.state.tasks.filter(task => task.id === id);
updatedTask[0].done = !updatedTask[0].done;
const newTasks = this.state.tasks.map(task => {
if (task.id === id) {
return updatedTask[0];
}
return task;
});
this.setState({
tasks: newTasks
});
}
render() {
return (
<div id="appContainer">
<HeaderTitle />
<TaskInput addTask={this.addTask} />
{
this.state.tasks.length > 0 ? <Tasks tasks={this.state.tasks} toggleTask={this.toggleTask}/> : <div>no tasks yet</div>
}
</div>
);
}
}
class TaskInput extends React.Component {
constructor(props) {
super(props);
this.state = {
currentInput: ''
}
}
handleChangeText = (e) => {
this.setState({
currentInput: e.target.value,
})
}
render() {
return (<form>
<input type="text" value={this.state.currenInput} onChange={this.handleChangeText}/><input type="submit" onClick={(e) => this.props.addTask(e, this.state.currentInput)} value="Add Task"/></form>)
}
}
const Tasks = (props) => (
<div>
{
props.tasks.map(task => (
<div
style={ task.done ? { textDecoration: 'line-through'} : {} }
onClick={() => props.toggleTask(task.id)}
>{task.text}</div>
))
}
</div>
);
const HeaderTitle = () => (
<h1>Davids Todo List</h1>
);
ReactDOM.render(<App />, document.getElementById('app'))
<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="app"></div>

How can I get <textarea> value in parent component from child?

The aim of my application is to get some array of messages(mes) from api-url and send answer(subFunction method) for every message. After that message will be delete from 'mes' array. 'Message' is a parent component responsible for fetch data(componentDidMount event) and rendering message through map method. 'MessageItem' responsible for get value from 'textarea' - the body of answer. But I can't transfer this.state.value(textarea.value) from from MessageItem to parent component. If I place 'subFunction' in child component, I can't change this.state.mes
import React from 'react'
import ReactDOM from 'react-dom'
const url="api-url";
class MessageItem extends React.Component {
constructor(props) {
super(props);
this.state = {
value:'',
};
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<div className="message_wrap" key={this.props.message_id}>
<div className="message_body">
{this.props.message_body}
</div>
<div className="input-field col s12">
<textarea value={this.state.value} onChange={this.handleChange.bind(this)}/>
<label htmlFor="textarea1">
Ответ
</label>
<button onClick={this.props.onClick}>
Отправить
</button>
</div>
</div>
);
}
}
class Message extends React.Component {
constructor(props) {
super(props);
this.state = {
mes:[],
};
};
componentDidMount(){
fetch(url).then(function(response){
return response
}).then(function (response) {
return response.json()
}).then((data)=>{
this.setState({mes:data})
})
}
subFunction(user_id, value) {
/*This method have to send answer with user id and textarea value*/
}
render() {
return (
<div>
{this.state.mes.map((index)=>
(
<MesItem
key={index.message_id}
message_body={index.message_body}
onClick={this.subFunction.bind(this, index.user_id)}
/>
)
)
}
</div>
);
}
}
ReactDOM.render(<Message/>, document.getElementById('container'));
You are passing a function from parent component to child, call that function to pass the value from child to parent.
Like this:
<button onClick={() => this.props.onClick(this.state.value)}>
Отправить
</button>
Now do console.log inside subFunction it will print proper value:
subFunction(user_id, value) {
console.log(user_id, value)
}
Assign a ref to the textarea in the child component and to the MesItem and then you can fetch the value like
class MessageItem extends React.Component {
constructor(props) {
super(props);
this.state = {
value:'',
};
mesItem = [];
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<div className="message_wrap" key={this.props.message_id}>
<div className="message_body">
{this.props.message_body}
</div>
<div className="input-field col s12">
<textarea value={this.state.value} ref={(ta) => {this.text = ta}}onChange={this.handleChange.bind(this)}/>
<label htmlFor="textarea1">
Ответ
</label>
<button onClick={this.props.onClick}>
Отправить
</button>
</div>
</div>
);
}
}
class Message extends React.Component {
constructor(props) {
super(props);
this.state = {
mes:[],
};
};
componentDidMount(){
fetch(url).then(function(response){
return response
}).then(function (response) {
return response.json()
}).then((data)=>{
this.setState({mes:data})
})
}
subFunction(user_id, i) {
console.log(this.mesItem[i].text.value)
}
render() {
return (
<div>
{this.state.mes.map((index, i)=>
(
<MesItem
ref = {(ip) => {this.mesItem[i] = ip}}
key={index.message_id}
message_body={index.message_body}
onClick={this.subFunction.bind(this, index.user_id , i)}
/>
)
)
}
</div>
);
}
}
ReactDOM.render(<Message/>, document.getElementById('container'));

Resources