React JSX with component in variable does not sync props - reactjs

The Scegli component is assigned to the App comp variable and then rendered in App render as a variable. However, the props assigned don't work: if I type in the input box, the value is frozen.
What am I doing wrong?
If I just move the <Scegli>...</Scegli> directly into the render (without assigning to a variable) it works as expected.
App.js
import React, { Component } from 'react';
import './App.css';
import Scegli from './components/Scegli';
class App extends Component {
constructor() {
super();
this.state = {
valore: 'Single'
}
this.comp = <Scegli value={this.state.valore} handleChange={this.setValoreHandler} />;
}
setValoreHandler = e => {
this.setState({
valore: e.target.value
});
}
render() {
return (
<div>
{this.comp}
</div>
);
}
}
export default App;
Scegli.js
import React, { Component } from 'react';
class Scegli extends Component {
render() {
return (
<div>
<input value={this.props.value} onChange={this.props.handleChange} />
Valore scelto: {this.props.value}
</div>
);
}
}
export default Scegli;

this.comp is declared once, at the component's mount and stays in that state. You are not updating/re-rendering it anywhere, that's why it remains unchanged.
You could either:
move the JSX component directly to render:
<div>
<Scegli value={this.state.valore} handleChange={this.setValoreHandler} />
</div>
or
update the class variable with every input change (not recommended though):
setValoreHandler = (e) => {
this.comp = <Scegli value={e.target.value} handleChange={this.setValoreHandler} />;
this.forceUpdate();
}

(I could be wrong) but when you define this.comp in the constructor() it is only loaded once with the default state. The constructor() is not called on re-render (similar to componentWillMount()). So that is why it is frozen as the updated state is never sent this.comp
Instead of this.comp in render do
return (
<div>
<Scegli value={this.state.valore} handleChange={this.setValoreHandler}/>
</div>
);

Related

How to call functional child component method from class parent component

I have a class based parent component like below
import React from "react";
import ReactDOM from "react-dom";
import FunChild from "./FunChild";
class App extends React.Component {
constructor(props) {
super(props);
this.childRef = React.createRef();
this.parentmethodFun = this.parentmethodFun.bind(this);
}
parentmethodFun() {
this.childRef.current.childmethod();
}
render() {
return (
<div>
<FunChild />
<button type="button" onClick={this.parentmethodFun}>
function
</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
the funChild.js file
import React from "react";
function FunChild(props) {
childmethod() {
console.log("child method is called");
}
return (<div>This is child ...!</div>);
}
export default FunChild;
if that child was a class component I can very easily use ref={this.childRef} to access child method.
But, it's a functional component and It was giving lot of problems. Can anyone please help me on this.
reference project link https://codesandbox.io/s/react-playground-forked-74xzn?file=/index.js
You should avoid this kind of relation because it is not the way how react works. In React you should pass everything from up to bottom. But if you reale want to achieve something like this you can use reference forwarding and imperative handler hook. E.g:
import { Component, forwardRef, createRef, useImperativeHandle } from "react";
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
childMethod() {
console.log("child method is called");
}
}));
return <div>This is child ...!</div>;
});
class App extends Component {
constructor(props) {
super(props);
this.childRef = createRef();
this.parentmethodFun = this.parentmethodFun.bind(this);
}
parentmethodFun() {
this.childRef.childMethod();
}
render() {
return (
<div>
<Child ref={(ref) => (this.childRef = ref)} />
<button type="button" onClick={this.parentmethodFun}>
function
</button>
</div>
);
}
}
Personally, i think you should rethink your application structure as it is very likely that there is a better solution than this trick.

React Adding two parents for a child component

State is the smart component which store all the states of child components
import React from 'react';
import TextArea from './Components/TextArea';
class State extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ''
}
this.myChangeHandler = this.myChangeHandler.bind(this)
}
myChangeHandler = (event) => {
event.preventDefault();
this.setState({
[event.target.name]:event.target.value
});
}
render() {
return (
<div>
{/* Change code below this line */}
<TextArea name = {this.state.name}
myChangeHandler = {this.myChangeHandler}/>
{/* Change code above this line */}
</div>
);
}
};
export default State;
Now TextArea is the child component which share the input value to state.
import React from 'react';
import '../style.css';
class TextArea extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<input type="text" onChange= {this.props.myChangeHandler} name="name" value={this.props.name}></input>
<h1>Hello, my name is:{this.props.name} </h1>
</div>
);
}
};
export default TextArea;
There are several child components that send the data to state component.
so the app component is used to order the child component.
I need two parents for a child component. please see the image enter image description here
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom'
import './App.css';
import TextArea from './Components/TextArea'
function App() {
return (
<div className="App">
<BrowserRouter>
<Route path = "/new" component= {TextArea} />
</BrowserRouter>
</div>
);
}
export default App;
You can't have two "direct" parent for a single component but you can have multiple indirect parents.
Example:
App is the parent of State
State is the parent of TextArea
App is also a parent of TextArea but not a direct one.
This means that you can pass props (data and functions) from the top parent to all of his children.
If you need to pass a function from App to TextArea you need to pass it by State.
Here a tutorial on how to do it.
You can also use the Context and here's a tutorial on how to do it.

Component render triggered, but DOM not updated

I'm having problems with my first React application.
In practice, I have a hierarchy of components (I'm creating a multimedia film gallery) which, upon clicking on a tab (represented by the Movie component) must show the specific description of the single film (SingleMovieDetails).
The problem is that the DOM is updated only on the first click, then even if the SingleMovieDetails props change, the DOM remains locked on the first rendered movie.
Here's the code i wrote so far...
//Movie component
import React from "react";
import styles from "./Movie.module.scss";
import PropTypes from "prop-types";
class Movie extends React.Component{
constructor(props){
super(props);
this.imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`;
}
render(){
if(!this.props.size)
return <div onClick={this.props.callbackClick(this.props.movie.id)}
name={this.props.movie.id}
className={styles.movieDiv}
style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
return <div onClick={() => this.props.callbackClick(this.props.movie.id)}
name={this.props.movie.id}
className={styles.movieDivBig}
style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
}
}
Movie.propTypes = {
movie: PropTypes.any,
callbackClick: PropTypes.any
};
export default Movie;
SingleMovieDetails.js
import React from "react";
import styles from "./SingleMovieDetails.module.scss";
import Movie from "../Movie";
import SingleMovieDescription from "../SingleMovieDescription";
import MovieCast from "../MovieCast";
import SingleMovieRatings from "../SingleMovieRatings";
class SingleMovieDetails extends React.Component{
constructor(props){
super(props);
console.log(props);
this.state = props;
console.log('constructor', this.state.movie)
}
render(){
console.log('SMD', this.state.movie)
return (
<>
<div className={styles.container}>
<div className={styles.flayer}>
<Movie size={'big'} movie={this.state.movie}/>
</div>
<div className={styles.description}>
<SingleMovieDescription movie={this.state.movie}/>
<MovieCast></MovieCast>
</div>
<div className={styles.ratings}>
<SingleMovieRatings />
</div>
</div>
</>
);
}
}
export default SingleMovieDetails;
MovieCarousel.js
import React from "react";
import PropTypes from "prop-types";
import Movie from "../Movie";
import styles from "./MovieCarousel.module.scss";
import SingleMovieDetails from "../SingleMovieDetails";
class MovieCarousel extends React.Component {
constructor(props) {
super(props);
this.state = [];
this.callbackClickMovie = this.callbackClickMovie.bind(this);
}
callbackClickMovie(id) {
const singleMovieApi = `https://api.themoviedb.org/3/movie/${id}?api_key=b6f2e7712e00a84c50b1172d26c72fe9`;
fetch(singleMovieApi)
.then(function(response) {
return response.json();
})
.then(data => {
this.setState({ selected: data });
});
}
render() {
let details = null;
if (this.state.selected) {
details = <SingleMovieDetails movie={this.state.selected} />;
}
let counter = 6;
let movies = this.props.movies.map(el => {
let element = (
<Movie movie={el} callbackClick={this.callbackClickMovie} />
);
counter--;
if (counter >= 0) return element;
return;
});
let content = (
<>
<h2 className={styles.carouselTitle}>{this.props.title}</h2>
{movies}
{details}
</>
);
return content;
}
}
MovieCarousel.propTypes = {
children: PropTypes.any
};
export default MovieCarousel;
I would be really grateful if someone could help me. I have been on it for two days but I can't really deal with it
This is because in SingleMovieDetails component, you are storing the props values in state and not updating the state on props change. constructor will not get called again so state will always have the initial values.
You have two options to solve the issue:
Directly use the props values instead of storing data in state (preferred way). Like this:
class SingleMovieDetails extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<>
<div className={styles.container}>
<div className={styles.flayer}>
<Movie size={'big'} movie={this.props.movie}/>
</div>
<div className={styles.description}>
<SingleMovieDescription movie={this.props.movie}/>
<MovieCast></MovieCast>
</div>
<div className={styles.ratings}>
<SingleMovieRatings />
</div>
</div>
</>
);
}
}
Use getDerivedStateFromProps, and update the state value on props change.
Same issue with Movie component also, put this line in the render method, otherwise it will always show same image:
const imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`
And use this imgUrl variable.
your Problem is just related to one file: SingleMovieDetails.js
Inside the constructor you´re setting the component state to get initialized with the props (send to the component the first time)
But inside your render() method you are referencing that state again:
<Movie size={'big'} movie={this.state.movie}/>
All in all thats not completely wrong, but you need to do one of two things:
Add a method to update your component state with the nextPropsReceived (Lifecycle Method was called: will receive props, if you are using the latest version you should use: getDerivedStateFromProps)
preferred option: you dont need a state for the movie component, so just use the props inside the render function (this.props.movie)
afterwards you can also delete the constructor, because there is nothing special inside. :)
edit:
So, just to be clear here: Since you´re only setting the state once (the constructor is not called on every lifecycle update) you will always only have the first value saved. Changing props from outside will just trigger render(), but wont start the constructor again ;D

How to click automatically in a button when user coming to page

import React from "react";
checkClick = () => {
console.log("clicked");
};
class Test extends React.Component {
render() {
return (
<div>
<button id="button" onClick={this.checkClick}>
click
</button>
</div>
);
}
}
export default Test;
How to click automatically on a button when user coming to page?
Here I want to click automatically above button.
I tried with:
document.getElementById("button").click()
which does not work.
You can use a ref which gives you an instance of the dom element, where you can call the click() method.
If you aren't familiar with refs, you can read all about them here: https://reactjs.org/docs/refs-and-the-dom.html
import React, { Component } from 'react'
class Test extends Component {
constructor(props) {
super(props)
this.button = React.createRef()
}
componentDidMount() {
this.button.current.click()
}
checkClick() {
console.log('clicked')
}
render() {
return (
<div>
<button ref={this.button} onClick={this.checkClick}>Click me!</button>
</div>
)
}
}
export default Test
First of all, I do not recommend you to create functions outside of React component class. In your case, you are not able to use it like this.checkClick because the checkClick function is declared outside of your React component.
The second thing, working with real DOM inside of React is basically, let's say, antipattern. React provides virtual DOM and works with it, so, I recommend you to learn about React ref API.
For your case, you can use the lifecycle componentDidMount() method. It is being called (AUTOMATICALLY, for sure) when the component has finished its first render, so, all refs are already available here and all children elements are beind mounted and present in DOM.
import React from "react"
export default class Test extends React.Component {
componentDidMount() {
document.getElementById("button").click()
}
checkClick() {
console.log("clicked!")
}
render() {
return (
<div>
<button id="button" onClick={this.checkClick}>click</button>
</div>
)
}
}
or, using refs
import React from "react"
export default class Test extends React.Component {
componentDidMount() {
this.button.click()
}
checkClick() {
console.log("clicked!")
}
render() {
return (
<div>
<button ref={button => this.button = button} onClick={this.checkClick}>click</button>
</div>
)
}
}
Use componentDidMount for that,
import React from 'react';
class Test extends React.Component {
componentDidMount(){
this.checkClick();
}
checkClick () {
console.log("clicked");
}
render() {
return (
<div>
<button id="button" onClick={this.checkClick}>click</button>
</div>
)
}
}
export default Test;

How do I communicate with my stateless component in React?

I wanted to create a very simple app containing a button. When I click on it it should change it's name and should change it's state to: isDisabled:true. I accomplished this by writing button inline, giving it an OnClick and so on, but I wanted to try this with stateless component with the same functionality, however I'm totally stuck.
import React, { Component } from 'react';
import './App.css';
class App extends Component{
constructor(){
super()
this.state = {name:'Hey buddy click me',
isDisabled:false,
}
}
render() {
return (
<div>
<MyButton handleClick={this.handleClick.bind(this)}></MyButton>
</div>
)
}
handleClick = () => {
this.setState ({
name:'Dont click!',
isDisabled:true,
});
}
}
const MyButton = (name, isDisabled) => <button onClick={this.handleClick.bind(this)}>{name}</button>
export default App;
You need to pass name, isDisabled too with handleClick as props if you want to access then in MyButton component.
Though name can also be passed as children. But as i feel you are learning start passing it as props.Then in future you can use children.
import React, { Component } from 'react';
import './App.css';
class App extends Component{
constructor(){
super()
this.state = {name:'Hey buddy click me',
isDisabled:false,
}
}
render() {
return (
<div>
<MyButton
name={this.state.name}
isDisabled={this.state.isDisabled}
handleClick={this.handleClick.bind(this)}>
</MyButton>
</div>
)
}
handleClick = () => {
this.setState ({
name:'Dont click!',
isDisabled:true,
});
}
}
//here we need to receive props from parent components, if we need to use here
const MyButton = ({name, isDisabled, handleClick}) => <button onClick={handleClick}>{name}</button>
export default App;

Resources