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>
Related
I am learning HOCs and keep reading the above quote, but I do not understand what it means. If my HOC adds a method to my consuming component, can I use that method in the render method like so? If not how would I do what I am trying to do here:
import React, { Component } from 'react';
import { withMyHOC } from '../with_my_component'
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default withMyHOC(MyComponent );
When you say, do not use HOC within the render method, it means that you shouldn't create an instance of the component wrapped by HOC within the render method of another component. For example, if you have a App Component which uses MyComponent, it shouldn't be like below
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default MyComponent;
import { withMyHOC } from '../with_my_component'
export default class App extends React.Component {
render() {
const Wrap = withMyHOC(MyComponent);
return (
<div>
{/* Other Code */}
<Wrap />
</div>
)
}
}
Why you shouldn't use it like above is because everytime render method is called a new instance of the MyComponent is created wrapped by HOC called Wrap and hence everytime it be be mounted again instead of going by the natural lifecycle or React.
However if your HOC passes a function as props, you can use it within the render as long as it doens't cause a re-render again otherwise it will lead to a infinite loop.
Also its better to memoize functions which are called in render directly to avoid computation again and again
CodeSandbox Demo
A High Order Component is a function which returns a Component, not jsx. When wrapping a component with an hoc, you're not changing the returned value of your component, you're changing the signature itself. Consider the following hoc
const withFoo = Component => props =>{
return <Component {...props} foo='foo' />
}
withFoo is a function which takes a Component (not jsx) as argument and returns a component. You don't need to call an hoc from render because the values it injects are already inside props of the wrapped component.
An hoc tells how a wrapped component will look like, changes it's definition so the only place to use it is in the component definition itself. Calling an hoc inside render creates a new instance of that component on each render. It's the equivalent of
const Component = () =>{
const ChildComponent = () =>{
return <span> Child </span>
}
return <ChildComponent /> //You're declaring again on each render
}
Use your high order components like this
const Component = ({ foo }) => <div>{ foo }</div>
export default withFoo(Component)
Or
const Component = withFoo(({ foo }) => <div>{ foo }</div>)
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 want to convert my code from stateless to stateful component in Typescript. I am a newbie, so it quite difficult to me. My code as below.
import React from 'react';
import { Route } from 'react-router-dom';
const AppRoute = ({ component: Component, layout: Layout, ...rest }: any) => (
<Route
{...rest}
render={(props) => (
<Layout>
<Component {...props} />
</Layout>
)}
/>
);
export default AppRoute;
It's not really different from non-Typescript stateful component syntax.
Take a look on an example from here (this link can be useful for you as a ts-react beginner):
import React, { Component } from 'react'; // let's also import Component
// the clock's state has one field: The current time, based upon the
// JavaScript class Date
type ClockState = {
time: Date
}
// Clock has no properties, but the current state is of type ClockState
// The generic parameters in the Component typing allow to pass props
// and state. Since we don't have props, we pass an empty object.
export class Clock extends Component<{}, ClockState> {
// The tick function sets the current state. TypeScript will let us know
// which ones we are allowed to set.
tick() {
this.setState({
time: new Date()
});
}
// Before the component mounts, we initialise our state
componentWillMount() {
this.tick();
}
// After the component did mount, we set the state each second.
componentDidMount() {
setInterval(() => this.tick(), 1000);
}
// render will know everything!
render() {
return <p>The current time is {this.state.time.toLocaleTimeString()}</p>
}
}
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.
I have a React component (Parent) within which there is another componet (Child) that is wrapped using a Higher Order function (HOWrapper). The problem I am having is that each time the Parent component renders, the Child component unmounts and then re-mounts. I need to find a way that prevents the Child component from unmounting, but still continue to wrap it in a component whose name is dynamically assigned. I also want the option to pass additional parameters to the HOWrapper function, that may also be dynamically generated in the render() function of Parent. Any ideas?
Parent component:
import { HOWrapper } from './somepath';
import Child from './someotherpath';
export default class Parent extends Component {
...
render(){
let ChildWrapper = HOWrapper(Child);
return (
<ChildWrapper {...this.props} />
);
}
}
Higher Order function:
export function HOWrapper(WrappedComponent) {
return class Blanket extends Component {
constructor(props) {
super(props);
this.state = {
...
};
}
...
render() {
return (
<WrappedComponent
{...this.props}
/>
);
}
}
}
Because an HOC returns a component, a more typical approach to using them is once when exporting them, not on every render call.
let YourComponent = props => <div />
export default HOC(YourComponent)
Then if you want to do anything dynamic pass new props to it.
render() { return <YourComponent dynamicProps={foo} /> }