Within this Avatar component, I want to set the state to match the props before it renders but the component only seems to update with the correct props in its render function.
eg.
main component gets data on its componentDidMount, passes that data to another component (Avatar), Avatar sets the state's data to match the passed data, UI renders with that data.
by default the redux store has avatar as avatar.png
class Account extends React.Component {
componentDidMount() {
this.props.fetchData();
}
render() {
const { data } = this.props
return(
<Switch>
<Route path="/avatar" exact={true} render={() => (<Avatar {...data} />)} />
</Switch>
)
}
}
class Avatar extends React.Component {
constructor(props) {
super(props)
console.log('constructor', this.props)
this.state = {}
}
componentDidMount() {
console.log('componentDidMount', this.props)
// this.setState({ data: this.props.data })
}
render() {
console.log('render', this.props)
return null
}
}
constructor {avatar: "avatar.png"}
test.js:62 render {avatar: "avatar.png"}
test.js:58 componentDidMount {avatar: "avatar.png"}
test.js:62 render {avatar: "something.png", other: "stuff"}
This happens because you're not waiting for the data from fetchData before rendering your Avatar component.
Since componentDidMount is only called once, on mount, it makes sense that you're only seeing your "correct" props in render.
Currently there is no way around this but in the future when React Suspense comes out this will be a slight non-issue.
Related
So, I want my component to render only when I do it (eg. when it is clicked.) . I don't want it to re-render when its props changes.
So, my idea was to have the parent pass a wasClicked='true' prop, when it is clicked, which I change to 'false' once it is rendered and have a condition in the render body to only render when wasClicked is true.
How is this possible?
With class components, you can decide if you want to update component with a lifecycle method called shouldComponentUpdate. With a functional component, you can decide if the component should update with React.memo
That should be easy, lets say this is a simple component that only renders when wasClicked is true:
simpleComponent.js:
import react from "react";
const SimpleComponent = (props) => {
return <p> some text </p>;
};
export default SimpleComponent;
App.js;
import react from "react";
import SimpleComponent from "./simpleComponent.js";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
wasClicked: false, //this is the state for wasClicked
};
}
//this is the function that changes wasClicked to true when the component is clicked
handleClick = () => {
this.setState({ wasClicked: true });
};
render() {
return (
<>
{this.state.wasClicked && ( //this will only render SimpleComponent when wasClicked is true
<SimpleComponent
onClick={this.handleClick}
wasClicked={this.state.wasClicked}
/>
)}
</>
);
}
}
export default App;
Hope this was helpful and clear, thanks
I understand the react higher order component example from the official docs but I want to use it slightly differently if possible, with props.children - ie
<PageHoc> // Higher order component
<Route exact path="/" component={Invite} /> // I want to auto inject props here
</PageHoc>
In my page HOC I can auto render out the child component but how can I attach some new props here?
import React from 'react';
class PageHoc extends React.Component {
constructor(props) {
super(props);
}
render() {
return this.props.children
}
}
export default PageHoc;
Your PageHoc component technically is just a parent component, not a HOC as it isn't wrapping and returning a new component. But you can still inject props into children component via react's Children helper and cloneElement.
import React, { Children, Component, createElement } from 'react';
class PageParent extends Component {
constructor(props) {
super(props);
}
render() {
return Children.map(
this.props.children,
child => cloneElement(child, { injectedProp: injectedPropValue })
);
}
}
export default PageParent;
As HOC
const withInjectedProps = WrappedComponent => {
const injectedProps = {
prop1: value1,
prop2: value2,
<...etc...>
};
return <WrappedComponent {...this.props} {...injectedProps} />
}
export default withInjectedProps;
const InjectedRoute = withInjectedProps(Route);
<InjectedRoute exact path="/" component={Invite} /> // has props injected
<InjectedRoute exact path="/a" component={OtherComponent} /> // has props injected too!
Kind of depends how you need to inject the props, if you have have just a single component, many, etc..
I forgot to mention that react HOCs by convention are named starting with "with", but this isn't the rule, i.e. react-redux's connect.
react HOC docs
One way is to clone the children and override the props like this,
import React from 'react';
class PageHoc extends React.Component {
constructor(props) {
super(props);
}
doSomething = () => {
//your logic
}
render() {
const childrenWithProps = React.Children.map(this.props.children, child =>
React.cloneElement(child, { doSomething: this.doSomething })
);
return <div>{childrenWithProps}</div>
}
}
export default PageHoc;
I am setting a state into child component on event perform and want to sent this to Parent component. I searched for this on SO. But still didn't found any way to do this.
Let say i have a parent component Home, and have child component User. I am performing some event in User component, and at that time, i want to pass data to Home component. How can i do this?
Below is my code:
/* Parent component */
import React, { Component } from 'react';
import User from './user';
class Home extends React.Component{
constructor(props){
super(props)
this.state = {
isReportSent: false
}
}
render(){
<Switch>
<Route exact path="/" component={User}/>
</Switch>
}
}
/* child component */
class User extends React.Component{
constructor(props){
super(props)
}
render(){
}
}
Note: My parent component is Routing component, in which i am routing my child component on particular path. So can't pass any function to child component.
import React, { Component } from "react";
class Home extends Component {
constructor(props) {
super(props);
this.state = {};
}
onChildAPICall = result => {
console.log(result);
};
render() {
return <User onAPICall={this.onChildAPICall} />;
}
}
class User extends Component {
constructor(props) {
super(props);
this.state = {};
this.API = "https://apicall";
}
makeAnAPICall = async () => {
let result = await fetch(this.API);
this.props.onAPICall(result);
};
render() {
return <button onClick={this.makeAnAPICall}>API Call</button>;
}
}
export default Home;
Something like this would work. I'm not sure if the below is 100% functioning as I just wrote it quickly but the idea is to pass down setState() as a prop from parent to child. So when child calls setState from props it's setting state in the parent component.
class Home extends React.Component {
constructor (props) {
super(props)
this.state = {
data: []
}
}
render () {
<ChilComponent setState={this.setState} />
}
}
const User = async ({ setState }) => {
const receivedData = await getDataHowever(params)
setState({
data: receivedData
})
return (
<p>Got data!</p>
)
}
You can call callback function of parent from child component and in parent you can set the state based on callback response.
import React, { Component } from "react";
class Home extends Component {
constructor(props) {
super(props);
this.state = { }
}
setStateOfParent= result => {
this.setState({result : result});
}
render() {
return <User setStateOfParent={this.setStateOfParent} />;
}
}
class User extends Component {
constructor(props) {
super(props);
this.state = {};
this.API = "https://apicall";
}
makeAnAPICall = async () => {
let result = await fetch(this.API);
this.props.setStateOfParent(result);
};
render() {
return <button onClick={this.makeAnAPICall}>API Call</button>;
}
}
export default Home;
Your explanation is not all clear what you want to acheive but as a simple pattern you can pass the callback prop to the child component using render method of react router
Parent Component
<Route exact path="/" render={(props) => <User {...props} callback={this.callback} />}/>
Child Class
this.props.callback(data)
#user10742206 The best way is to create an independent component and include it as a child in any parent component. Then you can pass a callback function from parent and child can use it to send back any data to parent.
export class Child extends React.Component{
unmount() {
const node = ReactDOM.getDOMNode(this);
ReactDOM.unmountComponentAtNode(node );
}
render() {
return <button onClick={this.unmount.bind(this)}>Unmount</button>
}
}
For the above sample component, would it possible to unmount it on click using unmountComponentAtNode?
React application is always a components composition, or in different words - components tree. It means that every component has parent component, which renders it, it is only falsy statement for root component, mostly we name it App, but we do not talk about it in that question. Above all, we can say that a component, which needs to be unmounted, always has a parent component.
As we did the assumption in first paragraph, the correct way of unmounting React component is to remove it from render method in parent component. It can be done in many ways, the simplest is just an conditional component rendering like:
class IAmParentComponent extends React.Component {
render() {
return (<div>{this.state.show && <YourChildComponentToUnmount/>}</div>)
}
}
Above example shows IAmParentComponent as container component which holds a state, and the YourChildComponentToUnmount will render if this.state.show is true, will unomount after state change from true to false.
Back to your code with callback, the callback should be send into component by props, and the parent component should do the state change related to removing the component from render tree, what exactly will start unmount phase of the component, and finally component will be removed from UI.
In conclusion, component unmount should be in responsibility of component above it, component should not unmount itself.
This is not the way react.
The best way to unmount an element is to tell the parent to remove the child from the rendered children of the parent.
Look at this example.
Here we have the CardContainer class and CardItem class.The CardItem item class has a delete self button. This method send an event to the parent container to remove itself from the rendered children.
const carddata = [
{key:1 ,name: 'Card A',visible:true},
{key:2 ,name: 'Card B',visible:true}
];
class CardItem extends React.Component {
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this);
}
componentWillUnmount(){
console.log('unmount')
}
handleClick(){
this.props.destroy(this.props.id)
}
render(){
return(<div>
Card
<button onClick={this.handleClick} >delete</button>
</div>)
}
}
class CardContainer extends React.Component {
constructor(props){
super(props)
this.state = {data: carddata};
this.destroy = this.destroy.bind(this);
}
destroy(elementKey){
console.log(elementKey)
debugger
let result = this.state.data.filter(item=> item.key !==elementKey);
this.setState({data: result});
}
render(){
return (<div>
Card Container
{this.state.data.map((card,index) => {
return <CardItem key={card.key} id={card.key} name={card.name} destroy={this.destroy}/>
})
}
</div>)
}
}
I've two components. One is parent (smart component that is connected to redux) and another is child component that is rendered in an iteration of array.
Whenever some redux action is dispatched from child component, the state in store is changed and whole list of elements is re-rendered but I want only to render the child that's actual state has been changed. Like in an array of locations, I want to show loader on a particular location and the object in array is updated well but why the shouldComponentUpdate is not available in child so that I can decide whether it should render or not.
Parent Component
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
class Locations extends React.Component {
constructor(props) {
super(props);
}
render() {
return this.props.locations.map((location) =>
<Location
toggleLocationStatusInfo={this.props.toggleLocationStatusInfo}
location={location} />)
}
}
const mapDispatchToProps = (dispatch) => ({
fetchLocations: (data) => dispatch(actions.fetchLocations(data)),
toggleLocationStatusInfo: dispatch(actions.toggleLocationStatusInfo()),
});
const mapStateToProps = createStructuredSelector({
locations: selectors.getLocations(),
});
export default connect(mapStateToProps, mapDispatchToProps)(Locations);
Child Component
import React from 'react';
class Location extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate() {
// THIS METHOD IS NEVER CALLED EVEN THE TOGGLE ACTION IS
// DISPATCHCED AND REDUX STATE IS CHANGED. IT IS CALLED FINE FINE
// PARENT (Locations) COMPONENT BUT NOT HERE
}
render() {
// render this.props.location content here
// One of my anchor calls onClick={this.props.toggleLocationStatusInfo}
}
}
Location.propTypes = {
location: React.PropTypes.object
toggleLocationStatusInfo: React.PropTypes.func,
}
How can I find out why the shouldComponentUpdate is not called in children?
I forgot to add a unique key on each component, so it was just creating new children every time.
After adding key prop on Location, it worked fine.
Same issue occurs if you are using Route from react-router and passing an inline function instead of a component, like so:
ISSUE
<Route
path="/decrement"
component={() => (
<Decrement decrementBy={decrementBy} isLoading={isLoading} />
)}
/>
SOLUTION
<Route
path="/decrement"
render={() => (
<Decrement decrementBy={decrementBy} isLoading={isLoading} />
)}
/>
or
<Route path="/decrement">
<Decrement decrementBy={decrementBy} isLoading={isLoading} />
</Route>