React component always gives last given props value - javascript

Child Component
export class MultiSelectDrawer extends React.Component {
componentDidMount(){
window.onpopstate = ()=>{
console.log(this.props.id);
}
}
render() {
const {input, id, label, showActionDrawer, toggleActionDrawer, items, closeActionDrawer} = this.props;
return (
<div>
</div>
);
}
}
MultiSelectDrawer.contextTypes = {
t: PropTypes.func.isRequired
}
export default MultiSelectDrawer;
Parent component
<td style={Object.assign({},styles.filters, styles.leftAlign)}>
<Field id="stateSearchFilter" name="stateSearchFilter" component={MultiSelectDrawer} label={this.context.t("State")} items={states} titleField='value' toggleActionDrawer = {toggleActionDrawerState} showActionDrawer = {this.state.showActionDrawerState} closeActionDrawer = {closeActionDrawerStateFilter} filterBar={filterBar}></Field>
</td>
<td style={Object.assign({},styles.filters, styles.leftAlign)}>
<Field id="prioritySearchFilter" name="prioritySearchFilter" component={MultiSelectDrawer} label={this.context.t("Priority")} items={priorities} titleField='label' toggleActionDrawer = {toggleActionDrawerPriority} showActionDrawer = {this.state.showActionDrawerPriority} closeActionDrawer = {closeActionDrawerPriorityFilter} filterBar={filterBar}></Field>
</td>
In the child componentDidMount window.onpopstate log I am expecting to see both the ids printed when browser back button is pressed. But it is only printing the second id which is prioritySearchFilter. I am quite new to React. What is wrong i am doing here?

The problem is that you are overwriting your first function.
You are calling:
window.onpopstate = ()=>{
console.log(this.props.id);
}
window.onpopstate = ()=>{
console.log(this.props.id);
}
twice (in each component instance). So its like when you assign two values to the same variable (here the variable is window.onpopstate). The last assignment overwrites the first.
var x = 1;
x = 2 // now x is 2. previous value is overwritten.
Or in your case:
window.onpopstate = func1;
window.onpopstate = func2;
After these two calls window.onpopstate now points to func2

Related

Can't access to a 'this' value in a function outside of constructor

I'm trying to learn how to make an app like reactjs but not really using it. I'm following a tutorial but I have some challenges. I have a function called 'update' which fires when there is a change in the state. I have a 'menu' object that I import as a child. The thing is, I can't seem to access to this child object in the 'update' function. Please have a look at the following:
import onChange from 'on-change';
import Menu from './menu';
class App {
constructor(){
const state = {
showMenu: false
}
this.state = onChange(state, this.update);
this.el = document.createElement('div');
this.el.className = 'todo';
// create an instance of the Menu
this.menu = new Menu(this.state);
// create a button to show or hide the menu
this.toggle = document.createElement('button');
this.toggle.innerText = 'show or hide the menu';
this.el.appendChild(this.menu.el);
this.el.appendChild(this.toggle);
// change the showMenu property of our state object when clicked
this.toggle.addEventListener('click', () => { this.state.showMenu = !this.state.showMenu; })
}
update(path, current, previous) {
if(path === 'showMenu') {
> // show or hide menu depending on state
> console.log (app.menu); // undefined
> this.menu[current ? 'show' : 'hide'](); // not working cause 'this.menu' is undefined
}
}
}
const app = new App();
> console.log (app.menu); // here it console logs correctly the object
document.body.appendChild(app.el);
Can someone help me to figure out what is going on here? thank you!
In the update() method in your App class, you have to use this.menu instead of app.menu.
It should look like this:
update(path, current, previous) {
if(path === 'showMenu') {
console.log (this.menu);
this.menu[current ? 'show' : 'hide']();
}
}
You can't use app within the App class because app is not defined there. You have to use the this keyword to access the members in the class itself.
Hope this helps.

Removing component with settimeout - React Class Component

I have a notification component, which when rendered, I remove it with some delay.
The problem is that when I try to use clearTimeout to cancel the removal, it doesn't. Look
class Notify extends React.Component {
constructor(props){
super(props)
this.class = 'check'
this.state = {load:LineNotify[this.class].anime}
this._delay()
this.timerS = null
this.box = BoxNotify[this.class]
this.icon = IconNotify[this.class]
}
remove=()=>{
// console.log('removed')
unmountComponentAtNode(document.getElementById('notify'))
}
_delay=()=>{
this.timerS = setTimeout(this.remove,2400)
}
stop=()=>{
clearTimeout(this.timerS)
this.setState({load:LineNotify[this.class].line})
}
start=()=>{
this.timerS = setTimeout(this.remove,2400)
this.setState({load:LineNotify[this.class].anime})
}
I have functions to control component removal, they must be called with cursor input, ie if user hovers, I should use clearTimeout to cancel removal ("start,stop")
remove=()=>{
console.log('removed')
// unmountComponentAtNode(document.getElementById('notify'))
}
When I don't use unmountComponentAtNode, using clearTimeout works as expected, but when using unmountComponentAtNode, clearTimeout is ignored and the component is removed anyway
I found a solution. Maybe somehow the timer ID returned by setTimeout is getting lost.
class Notify extends React.Component {
constructor(props){
super(props)
this.class = 'check'
this.state = {load:LineNotify[this.class].anime}
this.timerS = this.timer()
this.box = BoxNotify[this.class]
this.icon = IconNotify[this.class]
}
timer=()=>setTimeout(()=>{
document.getElementById('notify').remove()
},2400)
stop=()=>{
clearTimeout(this.timerS)
this.setState({load:LineNotify[this.class].line})
}
start=()=>{
this.timerS = this.timer()
this.setState({load:LineNotify[this.class].anime})
}

How to get return value each state changed in another componentin React

In a React Component Passed a value, if Component Change state, How to return a new value from this component.
If ScrollDetecter state changed how to return value this component?
<ScrollDetector/>
export default class ScrollDetector extends React.Component {
//Most impotant function only metioned
handleScroll() {
const windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
const body = document.body;
const html = document.documentElement;
const docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
const windowBottom = windowHeight + window.pageYOffset;
if (windowBottom >= docHeight) {
this.setState({
message:'bottom reached'
});
} else {
this.setState({
message:'not at bottom'
});
}
}
}
How to a particular value to parent component return if bottom reached message state.?
You want to change a parent component on event in child component(ScrollDetector)?
If so pass the callback function, that will update parent component, as a prop.
Let's say you want to change the state of parent component based on scroll. You would have something like this.
<ScrollDetector checkIfBottomReached={this.checkIfBottomReached} />
while the implementation of function would be
isBottomReached() {
this.setState({ isBottomReached: true });
}
or simply pass it shorthanded
<ScrollDetector checkIfBottomReached={() => this.setState({ isBottomReached: true })} />
and inside ScrollDetector component you would call this function when you reach the bottom, just by calling this.props.checkIfBottomReached()
You could use and event handler / callback function and send a prop from parent to child so it notifies when bottom has been reached:
class ScrollDetector extendsReactComponent {
handleScroll() {
...
if (windowBottom >= docHeight) {
this.props.onBottomReached()
} else {
...
}
}
}
and from your parent component:
class YourParent extends React.Component {
render() {
return (
<div>
<ScrollDetector
onBottomReached={() => console.log('bottom reached!') }
/>
</div>
)
}
}

Mixin functions only work in render()

For some reason, it appears that mixin functions in my code only work properly in render() function. It could be that I'm not calling them in the right manner outside of the render(), but shouldn't it be exactly the same way?
This way everything works fine (but I can't stick with this since I have to add some extra stuff to click handling, at the same time not altering the mixin):
var Row = React.createClass({
mixins: [someMixin]
},
render: function () {
var clickHandler = null;
var btn = null;
if (firstCase) {
clickHandler = this.order(this.props.name, this.props.else);
btn = (<a href="" onClick={clickHandler}>Order</a>);
} else if (secondCase) {
clickHandler = this.noOrder(this.props.name, this.props.else);
btn = (<a href="" onClick={clickHandler}>No order</a>);
}
return (
<div>
{btn}
</div>
);
}
});
But when I do the obvious and include the mixin functions in another function to handle the click - like this - everything fails and even 'test' is not printed in the console:
var Row = React.createClass({
mixins: [someMixin]
},
handleOrderClick(type) {
console.log('test');
if (type == 'order') {
this.order(this.props.name, this.props.else);
} else if (type == 'no-order') {
this.noOrder(this.props.name, this.props.else);
}
},
render: function () {
var clickHandler = null;
var btn = null;
if (firstCase) {
clickHandler = this.handleOrderClick('order');
btn = (<a href="" onClick={clickHandler}>Order</a>);
} else if (secondCase) {
clickHandler = this.handleOrderClick('no-order');
btn = (<a href="" onClick={clickHandler}>No order</a>);
}
return (
<div>
{btn}
</div>
);
}
});
EDIT:
order and noOrder functions look like this:
order: function (name, else) {
return function (event) {
event.preventDefault();
var term = name + '&&ยค%' + else;
Order.order(name, else, period, function (data) {
if (term === (global || window).MAIN_NAME + '.' + (global || window).MAIN) {
$(window).trigger('Name:update');
}
}.bind(this));
}.bind(this);
},
noOrder: function (name, else) {
return function (event) {
event.preventDefault();
if (!this.state.transferModalOpen) {
this.toggleTransferModal();
}
}.bind(this);
}
In order to use this.setState in handleOrderClick you'll have to use the bind method in your render method. Therefore handleOrderClick will become:
handleOrderClick(type, event) {
this.setState({foo: 'bar'});
if (type == 'order') {
this.order(this.props.name, this.props.else)(event);
} else if (type == 'no-order') {
this.noOrder(this.props.name, this.props.else)(event);
}
},
and your render method becomes:
render: function () {
var clickHandler = null;
var btn = null;
if (firstCase) {
clickHandler = this.handleOrderClick.bind(this, 'order');
btn = (<a href="" onClick={clickHandler}>Order</a>);
} else if (secondCase) {
clickHandler = this.handleOrderClick(this, 'no-order');
btn = (<a href="" onClick={clickHandler}>No order</a>);
}
return (
<div>
{btn}
</div>
);
}
You'll notice that the functions that are returned by this.order and this.noOrder are no longer returned by handleOrderClick, but are instead executed immediately. This should provide the effect you desire.
I've put the code in your example into a jsfiddle and it now seems to be working correctly. I've had to change the prop 'else' to 'alt' because 'else' is a reserved word. I've also just applied the mixin to the class directly for simplicity. I have simpilfied the order and noOrder functions as I don't have access to the Order object and we are only interested in them firing at the correct time. I've also added a second button that you can click to flip the cases so the other button is rendered, causing the component to render again. I've added a label that will display which function had been called when the button was last pressed.
for reference, you can find more information about the bind method here.
Hope this helps ^_^

how to render child component of only one generated child in react

In a React app, I have a component to display the results of a search called ResultsContainer. I am generating children by mapping a prop that has been passed down from a parent and creating a list element from each item in the array. Each of these children has a component that I (DetailsContainer) want to render when a button is clicked, and ONLY for the child that had the button clicked. As of now, the component renders for all of these children. I am not sure how to target the individual child so that the component only renders for that specific one.
ResultsContainer:
var React = require('react');
var DetailsContainer = require('./DetailsContainer.jsx');
var ResultsContainer = React.createClass ({
render: function () {
var queryType = this.props.queryType;
var details = this.props.details;
var that = this;
if (this.props.results == null) {
var searchResult = "Enter search terms"
} else {
var searchResult = this.props.results.map(function(result, index){
//if artist search
if (queryType == "artist") {
if (result.type == "artist") {
return <li className="collection-item" key={index}> {result.title} <button onClick={that.props.handleDetailClick}>Get details</button> <DetailsContainer details={details} /> </li> ;
}
}
});
};
return (
<div className="collection">
{searchResult}
</div>
);
},
});
module.exports = ResultsContainer;
When the button is clicked, I want to render the DetailsContainer component for that child ONLY, not all of them. How could I do this?
DetailsContainer:
var React = require('react');
var DetailsContainer = React.createClass({
render: function(){
if (this.props.details !== null) {
var detailsDisplay = "deets"
}
return (
<div className="detailsDisplay">
{detailsDisplay}
</div>
);
},
});
module.exports = DetailsContainer;
Here is handleDetailClick a function being passed down from the parent as a prop:
handleDetailClick: function() {
this.setState({details: "details"});
},
Also, in ResultsContainer, I could not figure out how to use .bind() to be able to use this.props.handleDetailClick from inside the callback of the .map, so i just created a variable "that" and set it equal to "this" outside the function. Is this bad practice? What is the proper way to do it using .bind()? Thanks so much.
First off map has an option to pass this. map(function(item){}, this); You have at least two options to limit the list. One is to use state like so;
render() {
let useList;
if (this.state.oneSelected)
useList = this.props.list[this.state.oneSelected];
else
useList = this.props.list;
let useListing = useList.map(function(item){}, this);
return ({useListing})
}
The other option is to pass the selected item to the parent this.props.selectOne(item.id) and have the parent return just the one item as a list.

Categories

Resources