react - how to pass event from another class - javascript

What I have
I have a class that's not being exported but being used internally by a file. (The classes are all in the same file)
class SearchResults extends Component {
constructor()
{
super();
this.fill_name = this.fill_name.bind(this);
}
fill_name(event, name)
{
console.log("search results", event.target);
this.props.fill_name(name, event.target);
}
render()
{
return (
<div className="search-results-item" onClick={ () => this.fill_name(event, this.props.name)}>
<div className="highlight">
{this.props.name}
</div>
</div>
)
}
}
I'm trying to get the <div> element to be sent back to the parent, which is defined below (skipping irrelevant stuff):
class PublishComponent extends Component {
fill_name(name, me)
{
console.log(me);
$("#company_input").val(name);
this.setState({ list: { ...this.state.list, company: [] } });
}
}
me is the event.
What I'm getting
The console posts the following:
search results <react>​</react>​
undefined
so the event.target is <react></react> for some reason, while the me is getting undefined.
Expected behaviour
It should return the element i.e. <div className="search-results-item"...></div>

You are not passing the event object
Change This
<div
className="search-results-item"
onClick={() => this.fill_name(event, this.props.name)}
/>
To This
<div
className="search-results-item"
// pass the event here
onClick={event => this.fill_name(event, this.props.name)}
/>

This should work for you:
class SearchResults extends Component {
constructor() {
super();
this.fill_name = this.fill_name.bind(this);
}
fill_name() {
this.props.fill_name(this.props.name, this.ref)
}
render() {
return (
<div className="search-results-item" ref={ref => { this.ref = ref }} onClick={this.fill_name}>
<div className="highlight">
{this.props.name}
</div>
</div>
)
}
}

Related

How to update state of one component in another in react class component. Save Functionality

How to update state of one component in another in react class component.
I have two class in reacts.
MyComponent and MyContainer.
export default class MyContainer extends BaseComponent{
constructor(props: any) {
super(props, {
status : false,
nameValue :"",
contentValue : ""
});
}
componentDidMount = () => {
console.log(this.state.status);
};
save = () => {
console.log("Hello I am Save");
let obj: object = {
nameValue: this.state.nameValue, // here I am getting empty string
templateValue: this.state.contentValue
};
// API Call
};
render() {
return (
<div>
<MyComponent
nameValue = {this.state.nameValue}
contentValue = {this.state.contentValue}
></MyComponent>
<div >
<button type="button" onClick={this.save} >Save</button>
</div>
</div>
);
}
}
MyComponent
export default class MyComponent extends BaseComponent{
constructor(props: any) {
super(props, {});
this.state = {
nameValue : props.nameValue ? props.nameValue : "",
contentValue : props.contentValue ? props.contentValue : "",
status : false
}
}
componentDidMount = () => {
console.log("MOUNTING");
};
fieldChange = (id:String, value : String) =>{
if(id === "content"){
this.setState({nameValue:value});
}else{
this.setState({contentValue:value});
}
}
render() {
return (
<div>
<div className="form-group">
<input id="name" onChange={(e) => {this.fieldChange(e)}}></input>
<input id = "content" onChange={(e) => {this.fieldChange(e)}} ></input>
</div>
</div>
);
}
}
In MyComponent I have placed two input field where on change I am changing the state.
Save button I have in MyContainer. In save button I am not able to read the value of MyComponent. What is the best way to achieve that.
You should be updating your state in MyContainer for save to have visibility of the state changes. Each component gets its own state, which makes MyComponent's state unique to that of MyContainer. What you should be doing is keeping the state in your parent/container component, and then passing it down as props (rather than duplicating it in your child). To do this, move fieldChange up to the MyContainer function, and remove the duplicate nameValue and contentValue state within MyComponent. See code commennts for further details:
export default class MyContainer extends BaseComponent{
...
fieldChange = (id:String, value : String) =>{
if(id === "content"){
this.setState({nameValue: value});
} else {
this.setState({contentValue: value});
}
}
render() {
return (
<div>
<MyComponent
nameValue={this.state.nameValue}
contentValue={this.state.contentValue}
onFieldChange={this.fieldChange} /* <---- Pass the function down to `MyComponent` */
/>
...
</div>
);
}
}
Then in MyComponent, call this.props.onFieldChange:
export default class MyComponent extends BaseComponent{
// !! this constructor can be removed as no state is being initialized anymore !!
constructor(props: any) {
super(props);
// removed state as we're using the state from `MyContainer`
}
componentDidMount = () => {
console.log("MOUNTING");
};
render() {
return (
<div>
<div className="form-group">
<input id="name" onChange={(e) => {this.props.fieldChange(e)}} /> /* <--- Change to `this.props.fieldChange()`. `<input />` is a self-closing tag.
<input id = "content" onChange={(e) => {this.props.fieldChange(e)}} />
</div>
</div>
);
}
}
Some additional notes:
If your component doesn't use this.props.children, then you should call it as <MyComponent ... props ... /> not <MyComponent ... props ...></MyComponent>
Your if-statement in your fieldChange looks reversed and should be checking if(id === "name"). I'm assuming this is an error in your question.
You're only passing one argument to fieldChange in your example code. I'm again assuming this in an error in your question.

Stop Relay: Query Renderer in reloading data for certain setStates

I'm currently following this and I did get it to work. But I would like to know if there is a way to stop the Query Render from reloading the data when calling this.setState(). Basically what I want is when I type into the textbox, I don't want to reload the data just yet but due to rendering issues, I need to set the state. I want the data to be reloaded ONLY when a button is clicked but the data will be based on the textbox value.
What I tried is separating the textbox value state from the actual variable passed to graphql, but it seems that regardless of variable change the Query will reload.
Here is the code FYR.
const query = graphql`
query TestComponentQuery($accountId: Int) {
viewer {
userWithAccount(accountId: $accountId) {
name
}
}
}
`;
class TestComponent extends React.Component{
constructor(props){
super(props);
this.state = {
accountId:14,
textboxValue: 14
}
}
onChange (event){
this.setState({textboxValue:event.target.value})
}
render () {
return (
<div>
<input type="text" onChange={this.onChange.bind(this)}/>
<QueryRenderer
environment={environment}
query={query}
variables={{
accountId: this.state.accountId,
}}
render={({ error, props }) => {
if (error) {
return (
<center>Error</center>
);
} else if (props) {
const { userWithAccount } = props.viewer;
console.log(userWithAccount)
return (
<ul>
{
userWithAccount.map(({name}) => (<li>{name}</li>))
}
</ul>
);
}
return (
<div>Loading</div>
);
}}
/>
</div>
);
}
}
Okay so my last answer didn't work as intended, so I thought I would create an entirely new example to demonstrate what I am talking about. Simply, the goal here is to have a child component within a parent component that only re-renders when it receives NEW props. Note, I have made use of the component lifecycle method shouldComponentUpdate() to prevent the Child component from re-rendering unless there is a change to the prop. Hope this helps with your problem.
class Child extends React.Component {
shouldComponentUpdate(nextProps) {
if (nextProps.id === this.props.id) {
return false
} else {
return true
}
}
componentDidUpdate() {
console.log("Child component updated")
}
render() {
return (
<div>
{`Current child ID prop: ${this.props.id}`}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
id: 14,
text: 15
}
}
onChange = (event) => {
this.setState({ text: event.target.value })
}
onClick = () => {
this.setState({ id: this.state.text })
}
render() {
return (
<div>
<input type='text' onChange={this.onChange} />
<button onClick={this.onClick}>Change ID</button>
<Child id={this.state.id} />
</div>
)
}
}
function App() {
return (
<div className="App">
<Parent />
</div>
);
}

How to move an index of a clicked item to another component that is not a parent?

Expecting effect: click <li> --> take index --> send this index to component Watch.
When I click <li>, I grab the index and move it to theWatch component. However, when I click the second li it returns the index of the one I clicked for the first time. I think this is because it updates this index via componentDidMount. How can I reference this index after componentDidMount?
Todo
class Todo extends Component {
render () {
return (
<div className = "itemTodos" onClick={()=> this.props.selectTodo(this.props.index)}>
</div>
)
}
}
export default Todo;
App
class App extends Component {
constructor(){
super();
this.state {
selectedTodoIndex: index
}
}
selectTodo = (index) => {
this.setState({
selectedTodoIndex: index
})
}
render () {
return (
<div>
<ul>
{
this.state.todos
.map((todo, index) =>
<Todo
key={index}
index={index}
todo={todo}
selectTodo ={this.selectTodo}
/>
)
}
</ul>
<Watch
selectedTodoIndex = {selectedTodoIndex}
/>
</div>
)
}
}
export default App;
Watch
class Watch extends Component {
constructor(){
super();
this.state = {
selectIndex: null
}
}
componentDidMount() {
this.setState({
selectIndex: this.props.selectedTodo
});
}
render () {
return (
<div>
</div>
)
}
}
First of all you you use selectedTodoIndex in
<Watch
selectedTodoIndex = {selectedTodoIndex}
/>
but it not specified in your render code. Add
const {selectedTodoIndex} = this.state;
in render function.
Second, use componentDidUpdate in Watch for update inner state on props update:
class Watch extends Component {
constructor(){
super();
this.state = {
selectIndex: null
}
}
componentDidMount() {
this.setState({
selectIndex: this.props.selectedTodo
});
}
componentDidUpdate (prevProps) {
if (prevProps.selectedTodo !== this.props.selectedTodo)
this.setState({
selectIndex: this.props.selectedTodo
});
}
render () {
return (
<div>
</div>
)
}
}
If i am not wrong your Todo component is in watch??. So Watch component should be like this :
render () {
return (
<div>
<Todo index={this.state.selectedIndex} selectedTodo={this.props.selectedTodoIndex}/>
</div>
)
}
Here i made codesandbox of this code . Feel free to checkout and let me know if you any doubt. Code link : https://codesandbox.io/s/frosty-chaplygin-ws1zz
There are lot of improvements to be made. But I believe what you are looking for is getDerivedStateFromProps lifeCycle method in Watch Component. So the code will be:
getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.selectedTodoIndex !== prevState.selectedTodoIndex) {
return { selectIndex: nextProps.selectedTodoIndex }
}
}
This will check if the selected index has changed in App Component, if yes it will update the state in Watch Component.

Easy communication of image between siblings

I'm new to ReactJS and I would like to communicate between my components.
When I click an image in my "ChildA" I want to update the correct item image in my "ChildB" (type attribute in ChildA can only be "itemone", "itemtwo", "itemthree"
Here is what it looks like
Parent.js
export default class Parent extends Component {
render() {
return (
<div className="mainapp" id="app">
<ChildA/>
<ChildB/>
</div>
);
}
}
if (document.getElementById('page')) {
ReactDOM.render(<Builder />, document.getElementById('page'));
}
ChildA.js
render() {
return _.map(this.state.eq, ecu => {
return (
<img src="../images/misc/ec.png" type={ecu.type_eq} onClick={() => this.changeImage(ecu.img)}/>
);
});
}
ChildB.js
export default class CharacterForm extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ name: "itemone" image: "defaultone.png"},
{ name: "itemtwo" image: "defaulttwo.png"},
{ name: "itemthree" image: "defaultthree.png"},
]
};
}
render() {
return (
<div className="items-column">
{this.state.items.map(item => (<FrameCharacter key={item.name} item={item} />))}
</div>
);
}
}
I can retrieve the image on my onClick handler in my ChildA but I don't know how to give it to my ChildB. Any hints are welcomed, thanks you!
What you need is for Parent to pass an event handler down to ChildA which ChildA will call when one of the images is clicked. The event handler will call setState in Parent to update its state with the given value, and then Parent will pass the value down to ChildB in its render method.
You can see this working in the below example. Since I don't have any actual images to work with—and to keep it simple—I've used <button>s instead, but the principle is the same.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
clickedItem: 'none',
};
}
render() {
return (
<div>
<ChildA onClick={this.handleChildClick}/>
<ChildB clickedItem={this.state.clickedItem}/>
</div>
);
}
handleChildClick = clickedItem => {
this.setState({ clickedItem });
}
}
const items = ['item1', 'item2', 'item3'];
const ChildA = ({ onClick }) => (
<div>
{items.map(name => (
<button key={name} type="button" onClick={() => onClick(name)}>
{name}
</button>
))}
</div>
);
const ChildB = ({clickedItem}) => (
<p>Clicked item: {clickedItem}</p>
);
ReactDOM.render(<Parent/>, document.querySelector('div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.development.js"></script>
<div></div>

Update React Grandparent State from onClick in Grandchild

I'm trying to use react to create a list of elements, and update the state of the parent of this list when a single element is clicked.
The overall container is App.jsx (the grandparent)
class App extends Component {
constructor(props) {
super(props);
this.state = {
selectedClass: null,
query: "cs 2110"
}
this.handleSelectClass.bind(this);
}
handleSelectClass(classId) {
console.log(classId);
//get the id of the course and get full course details
Meteor.call('getCourseById', classId, function(error, result) {
if (!error) {
console.log(this.state);
this.setState({selectedClass: result, query: ""}, function() {
console.log(this.state.selectedClass);
console.log(this.state.query);
});
} else {
console.log(error)
}
});
}
//check if a class is selected, and show a coursecard only when one is.
renderCourseCard() {
var toShow = <div />; //empty div
if (this.state.selectedClass != null) {
toShow = <CourseCard course={this.state.selectedClass}/>;
}
return toShow;
}
render() {
return (
<div className="container">
<header>
<h1>Todo List</h1>
</header>
<div className='row'>
<input />
<Results query={this.state.query} clickFunc={this.handleSelectClass}/>
</div>
<div className='row'>
<div className="col-md-6">
{this.renderCourseCard()}
</div>
<div className="col-md-6 panel-container fix-contain">
<Form courseId="jglf" />
</div>
</div>
</div>
);
}
}
The parent container is Results.jsx
export class Results extends Component {
constructor(props) {
super(props);
}
renderCourses() {
if (this.props.query != "") {
return this.props.allCourses.map((course) => (
//create a new class "button" that will set the selected class to this class when it is clicked.
<Course key={course._id} info={course} handler={this.props.clickFunc}/>
));
} else {
return <div />;
}
}
render() {
return (
<ul>
{this.renderCourses()}
</ul>
);
}
}
and the Course list item is a grandchild component
export default class Course extends Component {
render() {
var classId = this.props.info._id;
return (
<li id={classId} onClick={() => this.props.handler(classId)}>{this.props.info.classFull}</li>
);
}
}
I followed the suggestions here Reactjs - How to pass values from child component to grand-parent component? to pass down a callback function, but the callback still does not recognize the state of the grandparent. console.log(this.state) in App.jsx returns undefined even though the classId is correct, and the error says "Exception in delivering result of invoking 'getCourseById': TypeError: this.setState is not a function"
Is this a problem with the binding? I've tried this without Course as its own component and have the same issue.
Quickly looking through the code. I can see that problem one lies here. Even though you've bounded your function to your component, you're using a meteor call that scopes the result in it's own function scope which means that it won't be able to access this.setState. You can use fat arrow function to get around this problem, but you need to make sure that you are using ES6.
Meteor.call('getCourseById', classId, function(error, result) => {
if (!error) {
console.log(this.state);
this.setState({selectedClass: result, query: ""}, function() {
console.log(this.state.selectedClass);
console.log(this.state.query);
});
} else {
console.log(error)
}
});
TO
Meteor.call('getCourseById', classId, (error, result) => {
if (!error) {
console.log(this.state);
this.setState({selectedClass: result, query: ""}, () => {
console.log(this.state.selectedClass);
console.log(this.state.query);
});
} else {
console.log(error)
}
});
You've also binded your function incorrectly to your component.
this.handleClassSubmit = this.handleClassSubmit.bind(this);

Categories