Nested Function Returns Unexpected Result - reactjs

I have a menu component in my header component. The header component passes a function to the menu component => default menu component. It's working but the function returns unwanted data.
the path my function is traveling through is:
homepage => header => menu => defaultMenu
The function is:
changeBodyHandler = (newBody) => {
console.log(newBody)
this.setState({
body: newBody
})
}
I pass the function from homepage => header like this:
<HeaderDiv headerMenuClick={() => this.changeBodyHandler}/>
then through header => menu => defaultMenu using:
<Menu MenuClick={this.props.headerMenuClick} />
//==================== COMPONENT CODES ==========================//
homepage:
class Homepage extends Component {
constructor(props){
super(props);
this.state = {
body: "Homepage"
}
this.changeBodyHandler = this.changeBodyHandler.bind(this)
}
changeBodyHandler = (newBody) => {
console.log(newBody)
this.setState({
body: newBody
})
}
render() {
return (
<div>
<HeaderDiv headerMenuClick={() => this.changeBodyHandler}/>
{ this.state.body === "Homepage" ?
<HomepageBody />
: (<div> </div>)}
</div>
);
}
}
header:
class HeaderDiv extends Component {
constructor(props) {
super(props);
this.state = {
showMenu: 'Default',
}
}
render(){
return (
<Menu MenuClick={this.props.headerMenuClick}/>
);
}
}
menu:
import React, { Component } from 'react';
import DefaultMenu from './SubCompMenu/DefaultMenu';
import LoginCom from './SubCompMenu/LoginCom';
import SingupCom from './SubCompMenu/SingupCom';
class Menu extends Component {
//==================================================================
constructor(props){
super(props);
this.state = {
show: this.props.shows
};
this.getBackCancelLoginForm = this.getBackCancelLoginForm.bind(this);
}
//===============================================================
//getBackCancelLoginForm use to hindle click event singin & singup childs
//===============================================================
getBackCancelLoginForm(e){
console.log("Hi")
this.setState({
show : "Default"
})
}
//=================================================================
// getDerivedStateFromProps changes state show when props.shows changes
//===============================================================
componentWillReceiveProps(nextProps) {
if(this.props.show != this.nextProps){
this.setState({ show: nextProps.shows });
}
}
//======================================================================
render() {
return (
<div>
{ this.state.show === "Singin" ?
<LoginCom
cencelLogin={this.getBackCancelLoginForm.bind(this)}
/>
: (<div> </div>)}
{ this.state.show === "Singup" ?
<SingupCom
cencelLogin={this.getBackCancelLoginForm.bind(this)}
/>
: (<div> </div>)}
{ this.state.show === "Default" ?
<DefaultMenu MenuClicks={this.props.MenuClick}/> : (<div> </div>)}
</div>
);
}
}
Default menu:
class DefaultMenu extends Component {
render() {
return (
<div className="box11" onClick={this.props.MenuClicks("Homepage")}>
<h3 className="boxh3" onClick={this.props.MenuClicks("Homepage")}>HOME</h3>
);
}
}
//================ Describe expected and actual results. ================//
I'm expecting the string "Homepage" to be assigned to my state "body"
but console.log shows:
Class {dispatchConfig: {…}, _targetInst: FiberNode, nativeEvent: MouseEvent, type: "click", target: div.box11, …}
instead of "Homepage"

Use arrow functions in onClick listener, in above question Change DefaultMenu as:
class DefualtMenu extends Component {
render() {
return (
<div className="box11" onClick={() => this.props.MenuClicks("Homepage")}>
<h3 className="boxh3">HOME</h3>
</div>
);
} }
For arrow functions learn from mozilla Arrow Functions
I hope this helps.

Related

react child component not updating after sibling updates

I'm trying to update a sibling from another sibling but for some reasons it does not update in all cases.
I would appreciate if you can help me to find the issue.
So what I'm trying to do is to update InfoBox component from ProductSync component
I would like my output to look like this:
Case1(sync one product):
history:
Sync started ...
Message from Server
Case2 (sync multi products):
history:
Sync started ...
Message from Server
Sync started ...
Message from Server
Sync started ...
Message from Server
.
.
.
What I actually get is this:
Case1(sync one product):
history:
Message from Server
Case2 (sync multi products):
history:
(Dont get any more message here)
export class Admin extends React.Component {
constructor() {
super();
this.syncProduct = this.syncProduct.bind(this);
this.syncMultiProducts = this.syncMultiProducts.bind(this);
this.updateInfoBox = this.updateInfoBox.bind(this);
this.state = {
infoBox: ["history:"]
};
}
updateInfoBox(newText) {
const newStateArray = this.state.infoBox.slice();
newStateArray.push(newText);
this.setState({
infoBox: newStateArray
});
}
syncProduct(item) {
$.ajax({
datatype: "text",
type: "POST",
url: `/Admin/Sync`,
data: { code: item.Code },
async: false,
success: (response) => {
this.updateInfoBox (response.InfoBox);
},
error: (response) => {
this.updateInfoBox (response.InfoBox);
}
});
}
syncMultiProducts(items) {
/*this does not re-render InfoBox component*/
items.map((item, index) => {
this.syncProduct(item);
});
}
render() {
return (
<div>
<InfoBox infoBox={this.state.infoBox}/>
<ProductSync syncProduct={this.syncProduct} syncMultiProducts={this.syncMultiProducts} updateInfoBox={this.updateInfoBox}/>
</div>
);
}
}
ReactDOM.render(
<Admin />,
document.getElementById("admin")
);
First child(ProductSync.jsx):
export class ProductSync extends React.Component {
constructor() {
super();
this.syncProduct= this.syncProduct.bind(this);
}
syncProduct(item) {
this.props.updateInfoBox("Sync started...");/*this does not re-render InfoBox component*/
this.props.syncProduct(getItemFromDB());
}
syncMultiProducts() {
this.props.updateInfoBox("Sync started...");/*this does not re-render InfoBox component*/
this.props.syncMultiProducts(getItemsFromDB());
}
render() {
return (
<div>
<button onClick={() => this.syncMultiProducts()}>Sync all</button>
<button onClick={() => this.syncProduct()}>Sync one</button>
</div>
);
}
}
Second Child(InfoBox.jsx)
export class InfoBox extends React.Component {
constructor(props) {
super(props);
this.state = {
infoBox: props.infoBox
};
}
componentWillReceiveProps(nextProps) {
this.setState({ infoBox: nextProps.infoBox});
}
render() {
const texts =
(<div>
{this.state.infoBox.map((text, index) => (
<p key={index}>{text}</p>
))}
</div>);
return (
<div>
{Texts}
</div>
);
}
}
Thanks in advance

Why is this.state not updated real-time after setState is issued?

So I'm trying to simulate the state by clicking a button. The 'before' status seems to have the correct value, but why is the 'after' not displaying the correct value even if the setState is already hit by the code?
class App extends Component {
constructor(){
super()
this.state = {isLoggedIn: false}
this.OnClick = this.OnClick.bind(this);
}
OnClick(){
this.setState(prev =>
{
return (prev.isLoggedIn = !this.state.isLoggedIn);
})
console.log(`After setState value: ${this.state.isLoggedInstrong text}`) // setState is done, why is this.state displaying incorrect value?
}
render()
{
console.log(`Before setState value: ${this.state.isLoggedIn}`)
return <Login isLoggedIn={this.state.isLoggedIn} OnClick={this.OnClick} />
}
}
import React from "react";
class Login extends React.Component
{
render()
{
const {isLoggedIn, OnClick} = this.props;
return (
<div>
<button onClick={OnClick} >{isLoggedIn ? "Log Out" : "Log In"} </button>
</div>
)
}
}
export default Login;
OUTPUT:
"Before setState value: false"
(Initial display, button value is: Log In)
When button is clicked:
"After setState value: false" <------ why false when setState has been hit already? Not real-time update until Render is called?
"Before setState value: true"
(Button value is now: Log Out)
The main problem I see in your code is you’re trying to mutate the state.
this.setState(prev => {
return (prev.isLoggedIn = !this.state.isLoggedIn);
})
You have to merge to the state not mutate it. You can do it simply by returning an object like this.
this.setState((prev) => {
return { isLoggedIn: !prev.isLoggedIn };
});
This will fix all the weird behaviours in your code.
Full Code
App.js
import { Component } from "react";
import Login from "./Login";
class App extends Component {
constructor() {
super();
this.state = { isLoggedIn: false };
this.OnClick = this.OnClick.bind(this);
}
OnClick() {
this.setState((prev) => {
return { isLoggedIn: !prev.isLoggedIn };
});
console.log(`After setState value: ${this.state.isLoggedIn}`);
}
render() {
console.log(`Before setState value: ${this.state.isLoggedIn}`);
return <Login isLoggedIn={this.state.isLoggedIn} OnClick={this.OnClick} />;
}
}
export default App;
Login.js
import { Component } from "react";
class Login extends Component {
render() {
const { isLoggedIn, OnClick } = this.props;
return (
<div>
<button onClick={OnClick}>{isLoggedIn ? "Log Out" : "Log In"} </button>
</div>
);
}
}
export default Login;
CodeSandbox - https://codesandbox.io/s/setstate-is-not-update-the-state-69141369-efw46
try this
this.setState({
isLoggedIn:!this.state.isLoggedIn
})
or
this.setState(prev => ({
isLoggedIn:!prev.isLoggedIn
}))

React TransitionGroup lifecycle methods not being called when component is loaded

I'm trying to animate a list entering and exiting with gsap, so I wrapped my list with <TransitionGroup>. I want to use componentWillEnter and componentWillLeave to trigger my animations, but they aren't being called. I've checked everything 1000x and can't figure it out... Should I be using <Transition> instead for this sort of animation?
import React from "react";
import { TransitionGroup } from "react-transition-group";
import animations from './animations';
class Events extends React.Component {
componentWillEnter(cb) {
console.log('entered');
const items = document.getElementsByClassName("items");
animations.animateIn(items, cb);
}
componentWillLeave(cb) {
console.log('exited');
const items = document.getElementsByClassName("items");
animations.animateOut(items, cb);
}
render() {
const event = this.props.event;
return (
<li key={event._id} className="items">
<h1>{event.title}</h1>
</li>
);
}
}
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
events: []
};
}
componentDidMount() {
return fetch("https://pickup-btown.herokuapp.com/api/event/biking",
{
method: "GET",
headers: {
"Content-Type": "application/json"
},
mode: "cors"
})
.then(response => {
return response.json();
})
.then(events => {
this.setState({ events: events.docs });
})
.catch(err => console.log(err));
}
unLoad(e) {
e.preventDefault();
this.setState({ events: [] });
}
render() {
const events = this.state.events;
return (
<section>
<button onClick={this.unLoad.bind(this)}>back</button>
<TransitionGroup component="ul">
{events.length ? (
events.map(event => {
return <Events event={event} key={event._id} />;
})
) : (
<div />
)}
</TransitionGroup>
</section>
);
}
}
export default Main;
Any help would be much appreciated!
Child component life cycle methods has been removed in current react-transition-group and you can use onEnter and onExit methods to achieve like in your Events component will be
class Events extends React.Component {
render() {
const event = this.props.event;
return (
<Transition
timeout={300}
key={event._id}
onEntering={el => console.log("Entering", el)}
onEnter={el => console.log("enter", el)}
onExit={el => console.log("exit", el)}
in={true}
>
<li
key={event._id}
className="items"
>
<h1>{event.title}</h1>
</li>
</Transition>
);
}
}
I have worked on your codesandbox also and its working. For detailed info please go through documentation.

save react component and load later

I have react component in react native app and this will return Smth like this:
constructor(){
...
this.Comp1 = <Component1 ..... >
this.Comp2 = <Component2 ..... >
}
render(){
let Show = null
if(X) Show = this.Comp1
else Show = this.Comp1
return(
{X}
)
}
and both of my Components have an API request inside it ,
so my problem is when condition is changed and this toggle between Components , each time the Components sent a request to to that API to get same result ,
I wanna know how to save constructed Component which they wont send request each time
One of the ways do that is to handle the hide and show inside each of the child component comp1 and comp2
So you will still render both comp1 and comp2 from the parent component but you will pass a prop to each one of them to tell them if they need to show or hide inner content, if show then render the correct component content, else just render empty <Text></Text>
This means both child components exist in parent, and they never get removed, but you control which one should show its own content by the parent component.
So your data is fetched only once.
Check Working example in react js: https://codesandbox.io/s/84p302ryp9
If you checked the console log you will find that fetching is done once for comp1 and comp2.
Also check the same example in react native below:
class Parent extends Component {
constructor(props)
{
super(props);
this.state={
show1 : true //by default comp1 will show
}
}
toggleChild= ()=>{
this.setState({
show1 : !this.state.show1
});
}
render(){
return (
<View >
<Button onPress={this.toggleChild} title="Toggle Child" />
<Comp1 show={this.state.show1} />
<Comp2 show={!this.state.show1} />
</View>
)
}
}
Comp1:
class Comp1 extends Component
{
constructor(props) {
super(props);
this.state={
myData : ""
}
}
componentWillMount(){
console.log("fetching data comp1 once");
this.setState({
myData : "comp 1"
})
}
render(){
return (
this.props.show ? <Text>Actual implementation of Comp1</Text> : <Text></Text>
)
}
}
Comp2:
class Comp2 extends Component {
constructor(props) {
super(props);
this.state = {
myData2: ""
}
}
componentWillMount() {
console.log("fetching data in comp2 once");
this.setState({
myData2: "comp 2"
});
}
render() {
return (
this.props.show ? <Text>Actual implementation of Comp2</Text> : <Text></Text>
)
}
}
I think, you should move all your logic to the main component (fetching and saving data, so you component1 and component2 are simple dumb components. In component1 and component2 you can check "does component have some data?", if there isn't any data, you can trigger request for that data in parent component.
Full working example here: https://codesandbox.io/s/7m8qvwr760
class Articles extends React.Component {
componentDidMount() {
const { fetchData, data } = this.props;
if (data && data.length) return;
fetchData && fetchData();
}
render() {
const { data } = this.props;
return (
<div>
{data && data.map((item, key) => <div key={key}>{item.title}</div>)}
</div>
)
}
}
class App extends React.Component{
constructor(props){
super(props);
this.state = {
news: [],
articles: [],
isNews: false
}
}
fetchArticles = () => {
const self = this;
setTimeout( () => {
console.log('articles requested');
self.setState({
articles: [{title: 'article 1'}, {title: 'articles 2'}]
})
}, 1000)
}
fetchNews = () => {
const self = this;
setTimeout(() => {
console.log('news requested');
self.setState({
news: [{ title: 'news 1' }, { title: 'news 2' }]
})
}, 1000)
}
handleToggle = (e) => {
e.preventDefault();
this.setState({
isNews: !this.state.isNews
})
}
render(){
const { news, articles, isNews} = this.state;
return (
<div>
<a href="#" onClick={this.handleToggle}>Toggle</a>
{isNews? (
<News data={news} fetchData={this.fetchNews} />
): (
<Articles data={articles} fetchData={this.fetchArticles} />
)}
</div>
)
}
}

Passing value from Button to setState in array

I am trying to created boolean filters in an array.
If a button is "Active" (True) it should add the button name to the array ("selected").
If the button is "Inactive" (false), it should remove it from the array.
However, only some values end up in set state. I put it in a codepen:
https://codesandbox.io/s/wpxD35Oog
import React from 'react';
import { render } from 'react-dom';
import { x } from './data.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
movies: x.movies,
};
}
render() {
const uniqueGenres = []
.concat(
...this.state.movies.map(movies =>
movies.genres.map(genres => genres.name),
),
)
.filter((genre, i, _) => _.indexOf(genre) === i);
return (
<div>
<div>
{uniqueGenres.map(e => <Filter1 key={e} result={e} />)}
<br />
</div>
</div>
);
}
}
class Filter1 extends React.Component {
constructor(props) {
super(props);
this.state = {
active: true,
selected: '',
};
}
handleActive = e => {
// this.setState(previousState => ({
// selected: [...previousState.selected, e.target.value]
// }));
console.log('pre-setState', e.target.value);
const active = !this.state.active;
const selected = e.target.value;
this.setState({
active: active,
selected: selected,
});
console.log('status', this.state.active, this.state.selected);
};
render() {
return (
<span>
<button onClick={this.handleActive} value={this.props.result}>
{this.props.result} {}<b>
{this.state.active ? 'Active' : 'Inactive'}
</b>
</button>
</span>
);
}
}
Ideally you would let the parent component (in this case, App) take care of managing the active genres. I rewrote part of your code to demonstrate it: https://codesandbox.io/s/JZWp1RQED

Resources