Consumers re-render when Provider re-renders - javascript

According to react docs
the code below will re-render all consumers every time the Provider re-renders because a new object is always created for value
So i made a simple example to test this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
console.log('App');
return (
<>
<ThemeContext.Provider value={this.state.value}>
<ThemeContext.Consumer>
{(value)=>( <Toolbar test={value}/>)}
</ThemeContext.Consumer>
</ThemeContext.Provider>
<button onClick={this.handler}>click me</button>
</>
);
}
handler=()=>{
this.forceUpdate()
}
}
const app = <App />;
class Toolbar extends React.Component {
render() {
console.log('Toolbar');
return (
<div></div>
);
}
}
ReactDOM.render(app,mountNode);
It seems that in every click, even though the reference is the same, toolbar component re-renders along with provider. So what is wrong here?

Writing the consumer as direct children of App will cause them to render when App component re-renders instead you must write your code as
const ThemeContext = React.createContext();
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
console.log('App');
return (
<React.Fragment>
<ThemeContext.Provider value={this.state.value}>
{this.props.children}
</ThemeContext.Provider>
<button onClick={this.handler}>click me</button>
</React.Fragment>
);
}
handler=()=>{
this.forceUpdate()
}
}
const app = (<App>
<ThemeContext.Consumer>
{(value)=>( <Toolbar test={value}/>)}
</ThemeContext.Consumer>
</App>)
class Toolbar extends React.Component {
render() {
console.log('Toolbar');
return (
<div></div>
);
}
}
ReactDOM.render(app, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id="app"/>

try this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
handler(){
this.forceUpdate()
}
render() {
console.log('App');
return (
<div>
<ThemeContext.Provider value={this.state.value}>
<ThemeContext.Consumer>
{(value)=>( <Toolbar test={value}/>)}
</ThemeContext.Consumer>
</ThemeContext.Provider>
<button onClick={this.handler}>click me</button>
</div>
);
}
}
const app = <App />;
class Toolbar extends React.Component {
render() {
console.log('Toolbar');
return (
<div></div>
);
}
}
ReactDOM.render(app,mountNode);

Related

Click on a card and then navigate to card page with details ,in react

I am new to React and I am trying to make a simple application...I fetch some data from backend( a list with announces) and I make a list with them. So, I want that when I push to a button from that announce I want to go to another component where I can see details about that announce. I put my code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
carData: null,
checkData: false
}
}
getData = () => {
fetch('http://localhost:8000/api/all-articles')
.then( (response) => response.json() )
.then( (response) => {this.setState({
carData : response,
checkData : true
})});
}
componentDidMount = () => {
this.getData();
}
displayCars = () => {
return(
this.state.carData.data.map( (object, index) => (
<CarCard key={index} name = {object.title} description={object.description} img={object.image} id={object.id}/>
) )
);
}
render() {
if(this.state.checkData){
return(
<div className="App">
<Title/>
<div className="cars">
{this.displayCars()}
</div>
</div>
);
}else {
return(
<div>Loading..</div>
);
}
}
}
export default App;
class CarCard extends React.Component {
render() {
console.log(this.goToCardInfo(this.props.id));
return(
<div className="card">
<Card>
<Card.Img variant="top" src={`http://localhost:8000/images/${this.props.img}`}/>
<Card.Body>
<Card.Title>{this.props.name}</Card.Title>
<Card.Text>
{this.props.description}
</Card.Text>
<Button variant="primary">See announce</Button>
</Card.Body>
</Card>
</div>
);
}
}
export default CarCard;
class InfoCard extends React.Component {
render() {
return(
<h2>hello</h2>
);
}
}
export default InfoCard;
I want to say that from backend I fetch id, title,description, price..I googled it but I didn't understand how can I make it..Thank you
I think you need react-router-dom
I recommend you learn it. Below is my implementation with router.
App.js
import {BrowserRouter as Router, Switch, Route} from "react-router-dom"
import Cars from "./Cars.js" // Change it to the correct path
import InfoCard from "./InforCard.js"
class App extends React.Component {
render() {
<Router>
<Switch>
<Route path="/cars" component={Cars}></Route>
<Route path="/cars/:id" component={InfoCard} exact></Router>
<Switch>
</Router>
}
}
export default App;
Cars.js
class Cars extends React.Component {
constructor(props) {
super(props);
this.state = {
carData: null,
checkData: false
}
}
getData = () => {
fetch('http://localhost:8000/api/all-articles')
.then( (response) => response.json() )
.then( (response) => {this.setState({
carData : response,
checkData : true
})});
}
componentDidMount = () => {
this.getData();
}
displayCars = () => {
return(
this.state.carData.data.map( (object, index) => (
<CarCard key={index} name = {object.title} description={object.description} img={object.image} id={object.id}/>
) )
);
}
render() {
if(this.state.checkData){
return(
<div>
<Title/>
<div className="cars">
{this.displayCars()}
</div>
</div>
);
}else {
return(
<div>Loading..</div>
);
}
}
}
export default Cars;
CarCard.js
import { withRouter } from "react-router-dom"
class CarCard extends React.Component {
render() {
console.log(this.goToCardInfo(this.props.id));
return(
<div className="card">
<Card>
<Card.Img variant="top" src = {`http://localhost:8000/images/${this.props.img}`}/>
<Card.Body>
<Card.Title>{this.props.name}</Card.Title>
<Card.Text>
{this.props.description}
</Card.Text>
<Button variant="primary"
onClick={() => this.props.history.push("/card/" + this.props.id)}>See announce</Button>
</Card.Body>
</Card>
</div>
);
}
}
export default withRouter(CarCard);
InfoCard.js
class InfoCard extends React.Component {
// you can get the id of the card like this
const url = window.location.hash;
const id = url.substring(url.lastIndexOf("/") + 1);
// use that id to query your car
render() {
return(
<h2>hello</h2>
);
}
}
export default InfoCard;

switch icons on button click in react

I am using react-full-screen library.
Link to code sandbox
I have a navbar, where I have placed the JSX for the button with icons.
class AdminNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
isFfull: false
};
}
render() {
return (
<Navbar className="navbar" expand="lg">
<Container fluid>
<div className="navbar-wrapper">
<div className="navbar-minimize d-inline">
<button className="btn-fullscreen" onClick={this.props.goFull}>
<i className="fa fa-expand-arrows-alt"></i>
<i className="fa compress-arrows-alt"></i>
</button>
</div>
</div>
</Container>
</Navbar>
);
}
}
And then in my another Admin Component, I am using it as props and performing the onClick()
class Admin extends React.Component {
constructor(props) {
super(props);
this.state = {
isFull: false
};
}
goFull = () => {
if (document.body.classList.contains("btn-fullscreen")) {
this.setState({ isFull: true });
} else {
this.setState({ isFull: false });
}
document.body.classList.toggle("btn-fullscreen");
};
render() {
return (
<Fullscreen
enabled={this.state.isFull}
onChange={(isFull) => this.setState({ isFull })}
>
<div className="wrapper">
<div className="main-panel">
<AdminNavbar {...this.props} goFull={this.goFull} />
</div>
</div>
</Fullscreen>
);
}
}
Problem: the icons are not changing on click of the button. I also tried using the active class. but no luck.
You don't have to check the classList on body. The icon toggle can be achieved by state change.Please have a look at the code.
import React from "react";
import AdminNavbar from "./AdminNavbar";
import Fullscreen from "react-full-screen";
class Admin extends React.Component {
constructor(props) {
super(props);
this.state = {
isFull: false
};
}
goFull = () => {
this.setState({ isFull: !this.state.isFull });
};
render() {
return (
<Fullscreen
enabled={this.state.isFull}
onChange={(isFull) => this.setState({ isFull })}
>
<div className="wrapper">
<div className="main-panel">
<AdminNavbar
{...this.props}
isFull={this.state.isFull}
goFull={this.goFull}
/>
</div>
</div>
</Fullscreen>
);
}
}
export default Admin;
The admin Navbar code
import React from "react";
// reactstrap components
import { Navbar, Container } from "reactstrap";
class AdminNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
isFfull: false
};
}
render() {
return (
<Navbar className="navbar" expand="lg">
<Container fluid>
<div className="navbar-wrapper">
<div className="navbar-minimize d-inline">
<button className="btn-fullscreen" onClick={this.props.goFull}>
{!this.props.isFull ? (
<i className="fa fa-expand-arrows-alt"></i>
) : (
<i className="fa compress-arrows-alt"></i>
)}
</button>
</div>
</div>
</Container>
</Navbar>
);
}
}
export default AdminNavbar;

React Component does not render after the state changes

Hello I have a problem with re rendering the component below you can see my code for the first screen within that I have a custom component:
class Ahelle extends React.Component{
constructor(props){
super(props)
this.state={
mamad:10
}
}
render(){
return (
<View style={{backgroundColor:"white"}}>
<ScrollView>
<CustomSlider defaultValue={0} />
<CustomSlider defaultValue={this.state.mamad} disabled={true}/>
<TouchableOpacity style={{width:100,height:100,alignSelf:"center"}} onPress={()=>{this.setState({mamad:20})}}><Text>dddd</Text></TouchableOpacity>
</ScrollView>
</View>
);
}
}
Here I have a custom component and I pass a default value for showing but when I change the state , it is not changing the value I passed as props.
here is my custom slider component and its state, props and any other details.
class Test extends React.Component{
constructor(props){
console.log(props)
super(props)
this.state = {
value: props.defaultValue
};
}
render(){
return(
<Slider
style={{width: wp("70%")}}
value={this.state.value}
/>
)
}
}
export default Test;
SEE THE PROBLEM IN ACTION
Thank you for your time
Your slider component never does anything with updated prop values that are passed. Coincidentally, it's also a react anti-pattern to store passed props in local component state. You can, and should, consume them directly. Just pass this.state.mamad to the value prop and consume. You can also pass along any additional props by spreading them in to the slider component.
class Test extends React.Component {
render() {
return (
<Slider
style={{ width: wp("70%") }}
value={this.props.value}
{...this.props}
/>
);
}
}
export default Test;
Usage
<CustomSlider value={this.state.mamad} disabled={true} />
If you really wanted to store the passed defaultValue prop and keep it updated, then implement the componentDidUpdate lifecycle function. Note, this is not the recommended solution.
class Test extends React.Component {
constructor(props) {
console.log(props);
super(props);
this.state = {
value: props.defaultValue
};
}
componentDidUpdate(prevProps) {
const { defaultValue } = this.props;
if (prevProps.defaultValue !== defaultValue) {
this.setState({ value: defaultValue});
}
}
render() {
return <Slider style={{ width: wp("70%") }} value={this.state.value} />;
}
}
export default Test;
Your code is correct. I tested it.
App.js
import React from "react";
import { Text, View, ScrollView, TouchableOpacity } from "react-native";
import CustomSlider from "./CustomSlider";
export default class Ahelle extends React.Component {
constructor(props) {
super(props);
this.state = {
mamad: 10,
};
}
render() {
return (
<View style={{ backgroundColor: "white" }}>
<ScrollView>
<CustomSlider defaultValue={0} />
<CustomSlider defaultValue={this.state.mamad} disabled={true} />
<TouchableOpacity
style={{ width: 100, height: 100, alignSelf: "center" }}
onPress={() => {
this.setState({ mamad: 20 });
}}
>
<Text>dddd</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
}
CustomSlider.js
import React from "react";
import { Text } from "react-native";
export default class CustomSlider extends React.Component {
render() {
return <Text>defaultValue: {this.props.defaultValue}</Text>;
}
}
Result:
View Result

How to pass data from render method to multiple components in React?

I am new to React so looking like how can I render the data of image, name and user name from render method of one component to other components.
class Avatar extends React.Component {
render() {
return (
<img src={''} />
)
}
}
class Label extends React.Component {
render() {
return (
<h1>Name: </h1>
)
}
}
class ScreenName extends React.Component {
render() {
return (
<h3>Username: </h3>
)
}
}
class Badge extends React.Component {
render() {
return (
<div>
<Avatar />
<Label />
<ScreenName />
</div>
)
}
}
And the render method is this. How to read this image username and name into other components and update the view. Tried using {this.props.name} and also {this.props.user.name} but I am getting name as undefined.
ReactDOM.render(
<Badge user={{
name: 'Tyler McGinnis',
img: 'https://avatars0.githubusercontent.com/u/2933430?v=3&s=460',
username: 'tylermcginnis'
}} />,
document.getElementById('app')
);
And the HTML is this
<div id='app'></div>
You pass data via the component's props. It looks like this:
class Avatar extends React.Component {
render() {
return (
<img src={this.props.img} />
)
}
}
class Label extends React.Component {
render() {
return (
<h1>{this.props.name}</h1>
)
}
}
class ScreenName extends React.Component {
render() {
return (
<h3>{this.props.username}</h3>
)
}
}
class Badge extends React.Component {
render() {
return (
<div>
<Avatar img={this.props.user.img}/>
<Label name={this.props.user.name} />
<ScreenName username={this.props.user.username} />
</div>
)
}
}
And after some refactoring, you end up with this:
const Avatar = ({img}) => (
<img src={img} />
);
const Label = ({name}) => (
<h1>{name}</h1>
);
const ScreenName = ({username}) => {
<h3>{username}</h3>
);
const Badge = ({user}) => (
<div>
<Avatar img={user.img}/>
<Label name={user.name} />
<ScreenName username={user.username} />
</div>
)
Note that here we made use of so called functional stateless components, which can make your code a lot shorter and often more elegant. See here.
You can pass the data via props
https://codesandbox.io/s/o4nz576jn5

Resetting state on component in React

I have a simple problem in React JS. I have two different click events, which switch the state of the component. The first one works perfectly, however I cannot get the second event to reset the component back to its original state. This is a stripped down version of my problem, so just know that I cannot move the click functions into the Child component.
class Parent extends Component{
constructor(){
this.state = {
open: false
}
this.handleOpen = this.handleOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
}
handleOpen(){
this.setState({open: true})
}
handleClose(){
this.setState({open: false})
}
render(){
return(
<div>
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
</div>
)
}
}
Like I said, the handleOpen function switches the state, but the handleClose does not switch it back. I can get a console log to show on the handleClose function, so I know that it does not have to do with how it is being hooked up to the Child Component. Am I missing something about how to reset a state value after it has already been switched. Thank you for your help!
Here is How you have to do it!
class Child extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.props.isOpen);
if (this.props.isOpen) {
this.props.onClose();
} else {
this.props.onOpen();
}
}
render() {
return <button onClick={this.handleClick}>Click ME</button>;
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {
open: false
}
this.handleOpen = this.handleOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
}
handleOpen(){
this.setState({open: true})
}
handleClose(){
this.setState({open: false})
}
render(){
return(
<div>
<p>{this.state.open.toString()}</p>
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
</div>
)
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('container')
);

Categories