Rerendering React props.children - javascript

I have this kind of a setup:
// inside Parent.js
class Parent extends React.Component {
render() {
return { this.props.children }
}
}
// inside Child.js
class Child extends React.Component {
render() {
let message;
const greet = this.context.store.parentState.greet;
if(greet) message = 'Hello, world!';
return (
<div>
{ message }
</div>
)
}
}
Child.contextTypes = {
store: PropTypes.object.isRequired
}
// inside App.js
<Parent>
<Route path='/a-path' component={ Child } />
</Parent>
When Parent receives new state through setState, its render method is called but the render method in Child is not called!
The reason I want to achieve that is because some logic in Child is dependent on the state of Parent.
If I pass the state of Parent via context like how the store is passed and then access it in Child via this.context.parentState, this seems to be working and causing a call on Child's render method, I guess it's because we're receiving new context.
Why is this? context is great but is there a good way around this particular issue without needing context?

If you are rendering components as children, which aren't components to Route, you can make use of React.cloneElement with React.Children.map like
// inside Parent.js
class Parent extends React.Component {
render() {
return React.Children.map(this.props.children, (child) =>
React.cloneElement(child, {parentState: this.state.parentState})
);
}
}
However if elements rendered as Children to Parent are Routes then either you need to make use of context or write a wrapper around Route so that any extra props that the Route receives are passed on to the component
const RouteWrapper = ({exact, path, component: Component, anyOtherRouterProp, ...rest}) =>
<Route exact={exact} path={path} {...otherRouterProps} render={(props) => <Component {...props} {...rest} />} />
Where in the above case anyOtherRouterProp are the props that are applicable to the Route and needs to be destructured separately. After this you can make use of React.Children.map and React.cloneElement to pass on the props to children.
Although this is one such way, I would still recommend you to make use of context, specially with the introduction of new context API which makes it extremely easy to implement

You can do like this....
// inside Parent.js
class Parent extends React.Component {
render() {
return (
<Child children={this.props.children} />
)
}
}
// inside Child.js
class Child extends React.Component {
render() {
let message;
const greet = this.context.store.parentState.greet;
if(greet) message = 'Hello, world!';
return (
<div>
{ message }
{ this.props.children }
</div>
)
}
}
Child.contextTypes = {
store: PropTypes.object.isRequired
}
// inside App.js
<Parent>
<Route path='/a-path' component={ Child } />
</Parent>

Related

How can I pass attributes from a stateful component to an event handler used in an HOC wrapping a child component?

I am using a framework where I must pass an event handler into an HOC that is wrapping one of the children of my stateful Page component.
<Page>
<HOC onClick={fn}>
<PageColumn>
</PageColumn>
</HOC>
</Page>
I have a function in my stateful Page component that relies on the state and props of page. I must use a HOC that wraps a child component of the page. Within this HOC I must call an onClick method that relies on the state of the Page component.
So far I have tried passing down a reference to this.state in order to use the Page state in the HOC, and I have tried passing down props that are assigned the values that I needed in the parent state. In the onClick fn, whether I reference the necessary properties by using this.state or this.props, I get the error:
cannot read property '<attribute>' of undefined
How can I successfully implement this pattern?
There is not a lot to investigate from you code.
I noted that you use HOC as a component, but usually hoc are functions that add something to a component.
Usually hoc works like this:
EnrichedComponent = hoc(options)(BaseComponent);
example: react-redux connect hoc function
This approach should work:
// render Page somewhere
const App = () => (
<Page/>
)
// let Page component render the PageColumn component
class Page extends React.Component {
handleClick() {
// I can access page state and props
const {
state: { foo },
props: { bar },
} = this;
console.log(foo, bar);
}
render() {
return (
<PageColumn onClick={this.handleClick} />
)
}
}
// wrap component with HOC
const PageColumn = hoc()(() => {
return (
<div>...</div>
)
});
Take a look at the code below. Basically, you want to create the function in your top level component and bind it to this in the constructor. Then pass it down as a property to your child component (i.e. no parameter list, no function syntax, just `this.myFunc').
In the child component, in the onClick event, call the function and pass in your parameters. Use the name of the attribute of the parent function to call the function. So for example, here it's still this.props.myFunc, but if <Page /> had this expression instead: mySpecialFunc={this.myFunc} you would call like this instead <HOC onClick={(prm1,prm2,prm3) => this.props.mySpecialFunc(prm1,prm2,prm3)} />
In <Page />:
constructor(){
//regular constructor code
this.myFunc = this.myFunc.bind(this);
}
myFunc(prm1, prm2, prm3){\
//function code
//
this.setState({
myFuncState: "newValue",
//additional code
});
}
render(){
return (
<Page>
<HOC myFunc={this.myFunc}>
<PageColumn />
</HOC>
</Page>
)
}
In <HOC />:
render(){
prm1 = "someValue1";
prm2 = "someValue2";
prm3 = "someValue3";
return (
<div className="HOC" onClick={(prm1,prm2,prm3) => this.props.myFunc(prm1,prm2,prm3)}>
)
}
A Note on Parameters:
The above code is for the situation where all of your parameters are in one place (in this case, the direct child component). Sometimes they are spread out amongst parent, child, grandchild components, etc. In that case, you need to declare the parameters generically in each component, passing in the relevant parameters as you move down the chain of component ancestry. For example:
class Parent extends React.Component{
constructor(){
//normal render code
this.passedFunc = this.passedFunc.bind(this);
}
passedFunc(prm1,prm2){
//function code
this.setState({
a: val1,
b: val2,
//etc.
})
}
render(){
return (
<Child passedFunc={this.passedFunc}
)
}
}
class Child extends React.Component{
//other relevant code
render(){
let val1 = "foo";
return (
//we're passing prm1 into the func as val1. <Child /> doesn't know what prm2 is so we're keeping that generic and letting <GrandChild /> handle it
<GrandChild passedFunc={(prm1, prm2) => this.props.passedFunc(val1, prm2)}
)
}
}
class GrandChild extends React.Component{
//other relevant code
render(){
let val2 = "bar";
return (
//<GrandChild /> doesn't know what prm1 is, but it knows that prm2 is val2, so it's passing in prm2 as val2 and leaving prm1 generic so <Child /> can handle it
<div passedFunc={(prm1, prm2) => this.props.passedFunc(prm1, val2)}
)
}
As you can see, each component passes parameters up to their parent component and uses the generic parameter name for params they don't know. Finally, the parent component receives all of the parameters and is able to use them in the function. If there was a middle man component in the chain that didn't need to set any params, for example a component named between <Child /> and '' named <InBetween />, you could simply pass the function name through that component as a simple property, like this:
<InBetween passedFunc={this.props.passedFunc} />
Any parameters set in a component above or below it in the chain will remain intact.

React: parent component props in child without passing explicitly

Is it possible to have the props of the parent component to be available in child component without passing them down?
I am trying to implement a provider pattern, so that to access all the provider props in its child components.
EX:
Suppose the below provider comp FetchProvider will fetch the data and theme props on its own, and when any child component is enclosed by it, I want to access both props "data" and "theme" in the child component as well. How can we achieve it?
class FetchProvider
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
// do some
}
mapStateToProps()
{
return {data, theme};
}
}
class ChildComponent
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // is this possible here?
// do some
}
}
and if I try to above components as below.
<FetchProvider>
<ChildComponent name="some value"/> //how can we access parent component props here? without passing them down
<FetchProvider/>
This is exactly what react context is all about.
A Consumer can access data the a Provider exposes no matter how deeply nested it is.
// Context lets us pass a value deep into the component tree
// without explicitly threading it through every component.
// Create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton(props) {
// Use a Consumer to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
return (
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme} />}
</ThemeContext.Consumer>
);
}
Here is a small running example:
Note This is the react v16 context API.
Your use case can be solved with the usage of React context. With the help of Context, any child that is wrapped by a provided can be a consumer for the data that is provided by the Provider
In your case, you can use it like
context.js
export const FetchContext = React.createContext();
Provider.js
import { FetchContext } from 'path/to/context.js';
class FetchProvider extends React.Component
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
const { data, theme, children } = this.props;
return (
<FetchContext.Provider value={{ data, theme}}>
{children}
</FetchContext.Provider>
)
}
mapStateToProps()
{
return {data, theme};
}
}
ChildComponent.js
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
export default (props) => (
<FetchContext.Consumer>
{({ data, theme }) => <ChildComponent {...props} data={data} theme={theme} />}
</FetchContext.Consumer>
)
However given the fact that you are already using Redux, which is build on the concept of Context, you might as well use redux and access the values within the child component since they are the same values that are supplied from the Redux store to the child by parent.
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
const mapStateToProps = (state) => {
return {
data: state.data,
theme: state.theme
}
}
You can use React.Children to iterate over the children and pass whatever props you want to send to the new cloned elements using React.cloneElement.
EX:
class Parent extends React.Component {
constructor(props) {
super(props);
}
render() {
const { children } = this.props;
const newChildren = React.Children.map(children, child =>
React.cloneElement(child, { myProp: 'test' }));
return(
<View>
{newChildren}
</View>
)
}
}
Are you looking for:
class MyParent extends Component {
render() {
return <MyChild {...this.props}>
// child components
</MyChild>
}
}
This would pass all of the props passed into MyParent to the MyChild being rendered.

How to pass a wrapped component into React Router without it constantly remounting

We're in the process of upgrading our React App, and after many of hours of pain have realised that passing wrapped components into React Router (V4 and maybe others) causes the component to "remount" every time a new prop is passed in.
Here's the wrapped component...
export default function preload(WrappedComponent, props) {
class Preload extends React.Component {
componentWillMount() {
getDataForComponent(props);
}
render() {
return <WrappedComponent {...props} />;
}
}
return Preload;
}
And here's how we're using it...
const FlagsApp = (props) => {
return (
<Route path="/report/:reportId/flag/:id/edit" component{preload(FlagForm, props)} />
);
};
Anytime we're dispatching an action and then receiving a update, the component remounts, causing lots of problems. According to this thread on github, components will remount if:
you call withRouter(..) during rendering which would create a new component class each time
you pass a new function to Route.component each render, e.g. using anonymous function
{...}} />, which would create a new component as well
If I pass the FlagForm component in directly the problem is fixed, but then I can't take advantage of the preload function.
So, how can I achieve the same outcome, but without the component remounting?
Thanks in advance for any help!
The reason Route is mounting a new component on every update is that it's been assigned a new class each time via preload.
Indeed, each call to preload always returns a distinct anonymous class, even
when called with the same arguments:
console.log( preload(FlagForm,props) != preload(FlagForm,props) ) // true
So, since the issue is that preload being called within the FlagsApp component's render method, start by moving it outside of that scope:
const PreloadedFlagForm = preload(FlagForm, props) //moved out
const FlagsApp = (props) => {
return (
<Route path="/report/:reportId/flag/:id/edit"
component={PreloadedFlagForm} /> //assign component directly
);
};
This way the component for Route won't change between updates.
Now about that lingering props argument for preload: this is actually an anti-pattern. The proper way to pass in props just the standard way you would for any component:
const PreloadedFlagForm = preload(FlagForm) //drop the props arg
const FlagsApp = (props) => {
return (
<Route path="/report/:reportId/flag/:id/edit"
component={<PreloadedFlagForm {...props} />} //spread it in here instead
/>
);
};
And so the code for preload becomes:
export default function preload(WrappedComponent) {
class Preload extends React.Component {
componentWillMount() {
getDataForComponent(this.props);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return Preload;
}
Hope that helps!
If like me you didn't read the instructions, the answer lies in the render prop of the <Route> component
https://reacttraining.com/react-router/web/api/Route/render-func
render: func
This allows for convenient inline rendering and wrapping without the undesired remounting explained above.
So, instead of passing the wrapper function into the component prop, you must use the render prop. However, you can't pass in a wrapped component like I did above. I still don't completely understand what's going on, but to ensure params are passed down correctly, this was my solution.
My Preload wrapper function is now a React component that renders a Route...
export default class PreloadRoute extends React.Component {
static propTypes = {
preload: PropTypes.func.isRequired,
data: PropTypes.shape().isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}),
}
componentWillMount() {
this.props.preload(this.props.data);
}
componentWillReceiveProps({ location = {}, preload, data }) {
const { location: prevLocation = {} } = this.props;
if (prevLocation.pathname !== location.pathname) {
preload(data);
}
}
render() {
return (
<Route {...this.props} />
);
}
}
And then I use it like so...
const FlagsApp = (props) => {
return (
<Switch>
<PreloadRoute exact path="/report/:reportId/flag/new" preload={showNewFlagForm} data={props} render={() => <FlagForm />} />
<PreloadRoute exact path="/report/:reportId/flag/:id" preload={showFlag} data={props} render={() => <ViewFlag />} />
<PreloadRoute path="/report/:reportId/flag/:id/edit" preload={showEditFlagForm} data={props} render={() => <FlagForm />} />
</Switch>
);
};
The reason I'm calling this.props.preload both in componentWillMount and componentWillReceiveProps is because I then had the opposite issue of the PreloadRoute component not remounting when navigating, so this solves that.
Hopefully this save lots of people lots of time, as I've literally spent days getting this working just right. That's the cost of being bleeding edge I guess!

Communicating between React components

I'm new to react so this is something I don't know. In the app that I
'm working with it has a main component where other components are loaded.
Like this,
render() {
return (
<div className="index">
<HeaderComponent />
<MainHeroComponent />
<AboutComponent />
</div>
);
}
And I want when someone clicks a link in HeaderComponent to show the about component. And hide the MainHeroComponent. How can I do such communication between components in React? Is it possibe?
Thanks
Use React-Router and create routes for this scenario instead of direct communication between components. Sample app structure using react-router
const App = React.createClass({
render() {
return (
<div>
<h1>App</h1>
<HeaderComponent />
</div>
)
}
})
render((
<Router>
<Route path="/" component={App}>
<Route path="hero" component={MainHeroComponent} />
<Route path="about" component={AboutComponent} />
</Route>
</Router>
), document.body)
For more details on router refer: https://github.com/reactjs/react-router/blob/master/docs/guides/RouteConfiguration.md
Aditya's answer is probably a better solution, but if you really want to it your way, you can use state and callbacks.
class Index extends React.Component {
constructor(props) {
super(props);
this.state = {
showHero: true
};
this.toggleShowHero = this.toggleShowHero.bind(this);
}
toggleShowHero() {
this.setState({
showHero: !this.state.showHero
});
}
render() {
return (
<div className="index">
<HeaderComponent onClick={toggleShowHero}/>
{
this.state.showHero ?
<MainHeroComponent /> :
<AboutComponent />
}
</div>
);
}
There are various ways you can achieve this, including React-routers and Redux, but since you're new to React, it'll be good if you get familiar with the basics first. For a start, you have to change the state of the main component to decide which child component to render.
In the main component code snippet you posted, initialize a state in the constructor as follows:
/* in the main component */
constructor(props) {
super(props);
this.state = {
showAbout: true
};
}
Then modify the render function as follows, to pass a reference to your main component, down to your header component:
/* in the main component */
<HeaderComponent mainComponent={this}/>
Then, in HeaderComponent, attach a click event handler to the link on which you want to perform the operation.
/* in HeaderComponent */
<a href="#" ....... onClick={this.showAbout.bind(this)}>Show About</a>
In the same component, define the showAbout function as follows:
/* in HeaderComponent */
showAbout () {
let mainComponent = this.props.mainComponent;
mainComponent.setState({
showAbout: true
)};
}
Finally, back in the render function of the main component:
/* in the main component */
render () {
let mainHeroComponent, aboutComponent;
if (this.state.showAbout) {
aboutComponent = (
<AboutComponent/>
);
} else {
mainHeroComponent = (
<MainHeroComponent/>
);
}
return (
<div className="index">
<HeaderComponent mainComponent={this}/>
{mainHeroComponent}
{aboutComponent}
</div>
);
}
And you're done! Basically, a component gets re-rendered every time its state is changed. So each time you click on the link, the main component's state is changed with a new value of showAbout. This will cause the main component to re-render itself, and, based on the value of showAbout, it will decide whether to render MainHeroComponent or AboutComponent.
But you should make sure you have a similar logic to display MainHeroComponent as well, instead of AboutComponent, just to switch the views.

How to pass in a react component into another react component to transclude the first component's content?

Is there a way to pass one component into another react component? I want to create a model react component and pass in another react component in order to transclude that content.
Edit: Here is a reactJS codepen illustrating what I'm trying to do. http://codepen.io/aallbrig/pen/bEhjo
HTML
<div id="my-component">
<p>Hi!</p>
</div>
ReactJS
/**#jsx React.DOM*/
var BasicTransclusion = React.createClass({
render: function() {
// Below 'Added title' should be the child content of <p>Hi!</p>
return (
<div>
<p> Added title </p>
{this.props.children}
</div>
)
}
});
React.renderComponent(BasicTransclusion(), document.getElementById('my-component'));
You can use this.props.children to render whatever children the component contains:
const Wrap = ({ children }) => <div>{children}</div>
export default () => <Wrap><h1>Hello word</h1></Wrap>
Note I provided a more in-depth answer here
Runtime wrapper:
It's the most idiomatic way.
const Wrapper = ({children}) => (
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
);
const App = () => <div>Hello</div>;
const WrappedApp = () => (
<Wrapper>
<App/>
</Wrapper>
);
Note that children is a "special prop" in React, and the example above is syntactic sugar and is (almost) equivalent to <Wrapper children={<App/>}/>
Initialization wrapper / HOC
You can use an Higher Order Component (HOC). They have been added to the official doc recently.
// Signature may look fancy but it's just
// a function that takes a component and returns a new component
const wrapHOC = (WrappedComponent) => (props) => (
<div>
<div>header</div>
<div><WrappedComponent {...props}/></div>
<div>footer</div>
</div>
)
const App = () => <div>Hello</div>;
const WrappedApp = wrapHOC(App);
This can lead to (little) better performances because the wrapper component can short-circuit the rendering one step ahead with shouldComponentUpdate, while in the case of a runtime wrapper, the children prop is likely to always be a different ReactElement and cause re-renders even if your components extend PureComponent.
Notice that connect of Redux used to be a runtime wrapper but was changed to an HOC because it permits to avoid useless re-renders if you use the pure option (which is true by default)
You should never call an HOC during the render phase because creating React components can be expensive. You should rather call these wrappers at initialization.
Note that when using functional components like above, the HOC version do not provide any useful optimisation because stateless functional components do not implement shouldComponentUpdate
More explanations here: https://stackoverflow.com/a/31564812/82609
const ParentComponent = (props) => {
return(
{props.childComponent}
//...additional JSX...
)
}
//import component
import MyComponent from //...where ever
//place in var
const myComponent = <MyComponent />
//pass as prop
<ParentComponent childComponent={myComponent} />
You can pass it as a normal prop: foo={<ComponentOne />}
For example:
const ComponentOne = () => <div>Hello world!</div>
const ComponentTwo = () => (
<div>
<div>Hola el mundo!</div>
<ComponentThree foo={<ComponentOne />} />
</div>
)
const ComponentThree = ({ foo }) => <div>{foo}</div>
Facebook recommends stateless component usage
Source: https://web.archive.org/web/20160608001717/http://facebook.github.io/react/docs/reusable-components.html
In an ideal world, most of your components would be stateless
functions because in the future we’ll also be able to make performance
optimizations specific to these components by avoiding unnecessary
checks and memory allocations. This is the recommended pattern, when
possible.
function Label(props){
return <span>{props.label}</span>;
}
function Hello(props){
return <div>{props.label}{props.name}</div>;
}
var hello = Hello({name:"Joe", label:Label({label:"I am "})});
ReactDOM.render(hello,mountNode);
i prefer using React built-in API:
import React, {cloneElement, Component} from "react";
import PropTypes from "prop-types";
export class Test extends Component {
render() {
const {children, wrapper} = this.props;
return (
cloneElement(wrapper, {
...wrapper.props,
children
})
);
}
}
Test.propTypes = {
wrapper: PropTypes.element,
// ... other props
};
Test.defaultProps = {
wrapper: <div/>,
// ... other props
};
then you can replace the wrapper div with what ever you want:
<Test wrapper={<span className="LOL"/>}>
<div>child1</div>
<div>child2</div>
</Test>
You can pass in a component via. the props and render it with interpolation.
var DivWrapper = React.createClass({
render: function() {
return <div>{ this.props.child }</div>;
}
});
You would then pass in a prop called child, which would be a React component.
Late to the game, but here's a powerful HOC pattern for overriding a component by providing it as a prop. It's simple and elegant.
Suppose MyComponent renders a fictional A component but you want to allow for a custom override of A, in this example B, which wraps A in a <div>...</div> and also appends "!" to the text prop:
import A from 'fictional-tooltip';
const MyComponent = props => (
<props.A text="World">Hello</props.A>
);
MyComponent.defaultProps = { A };
const B = props => (
<div><A {...props} text={props.text + '!'}></div>
);
ReactDOM.render(<MyComponent A={B}/>);
Actually, your question is how to write a Higher Order Component (HOC). The main goal of using HOC is preventing copy-pasting. You can write your HOC as a purely functional component or as a class here is an example:
class Child extends Component {
render() {
return (
<div>
Child
</div>
);
}
}
If you want to write your parent component as a class-based component:
class Parent extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
If you want to write your parent as a functional component:
const Parent = props => {
return (
<div>
{props.children}
</div>
);
}
Here is an example of a parent List react component and whos props contain a react element. In this case, just a single Link react component is passed in (as seen in the dom render).
class Link extends React.Component {
constructor(props){
super(props);
}
render(){
return (
<div>
<p>{this.props.name}</p>
</div>
);
}
}
class List extends React.Component {
render(){
return(
<div>
{this.props.element}
{this.props.element}
</div>
);
}
}
ReactDOM.render(
<List element = {<Link name = "working"/>}/>,
document.getElementById('root')
);
Let's create a Wrapper Component:
export const Wrapper = (props) => {
return(<>
<Menu />
{props.children}
<Footer />
</>
)
}
You can now enclose your new into an existing structure.
You will enclose the Component in a Route for example:
<Route path="/" element={<Wrapper><ExampleComponent /></Wrapper>} />
You can pass your component as a prop and use the same way you would use a component.
function General(props) {
...
return (<props.substitute a={A} b={B} />);
}
function SpecificA(props) { ... }
function SpecificB(props) { ... }
<General substitute=SpecificA />
<General substitute=SpecificB />
you can pass your react component into another component and emit the function from child
import CustomerFilters;
parent:
const handleFilterChange = (value) => {
console.log(value)
}
<DataGrid
contentName="customer"
fetchFilterComponents = {<CustomerFilters onSelectFilter={handleFilterChange} />}
</DataGrid>
child:
CustomerFilters
return (
<select className="filters-dropdown" onChange={onSelectFilter}>
<option>Select Filter</option>
{customerFilterOptions?.map((filter: any) => {
return <option value={filter.value}>{filter.name}</option>;
})}
</select>
)

Categories

Resources