Child component changing parent component state - reactjs

Here there is app root component passing a handleHeaderTitle function to all its children by cloning.
Inside children components, the function is called in componentWillMount()
and as a result root component updates header text based on route.
Root :
{
this.props.children &&
React.cloneElement(this.props.children, {
handleHeaderTitle: this.handleHeaderTitle
})
}
handleHeaderTitle(title) {
this.setState({ headerTitle: title });
}
Child:
componentWillMount() {
this.props.handleHeaderTitle("Profile");
}
Profile.propTypes = { handleHeaderTitle: React.PropTypes.func };
Is this right usecase for adding redux since the state is not really local here as is being set from a child component on its parent ?
I think I will need to create actions like SET_PROFILE_HEADER and inside reducers, combine these. But still figuring out how to inform container root component that child component has changed the header title

To answer your question directly, you don't need Redux to solve your problem. Redux helps you when you have to maintain a lot of state, or when your state needs to be reflected parts of the application that don't have a close common ancestor. In other words, when it gets too cumbersome to use good old fashioned React state.
A more common approach to your specific problem in React would be to include your layout in the child component instead of the root. This avoids the issue of trying to get the child to control content in the parent. It helps preserve the "unidirectional data flow" which is what React is all about.
class Page extends React.Component {
render() {
return (
<div>
<div className='profile-header'>
{ this.props.headerTitle }
</div>
<div className='profile-content'>
{ this.props.children }
</div>
</div>
)
}
}
class MyFirstPage extends React.Component {
render() {
return (
<Page headerTitle='Header Title 1'>
<div>My content here</div>
</Page>
)
}
}

Related

How to render component depends role

so I have a specific code.
The problem is it's not works in moments - when it's should.
I need refresh page to works.
If user from LocalStorage is null it's should render
If user from LocalStorage exist it's shouldn't render
It's works, but how can I improve it to works after login (not after refreshing page) ?
Should I use other react lifecycle method?
class Navbar extends React.Component {
constructor(props){
super(props)
this.state ={
user: null
}
}
componentDidMount(){
const user = JSON.parse(localStorage.getItem('user'));
this.setState({
user: user
},
console.log('test'+ this.state.user)
)
}
render(){
return(
<div>
{ !this.state.user ?
<div className ="navbar">
<Logo/>
<Menu/>
<PanelOptions/>
</div>
: null}
</div>
);
}
}
Looking at the component it seems that it can be a functional component.
And the user can be sent as a prop from parent component which is rendering the NavBar component.
export default NavBar = (props) => {
if(!props.user)
{
return (
<div className ="navbar">
<Logo/>
<Menu/>
<PanelOptions/>
</div>
)
}
else
{
return null
}
}
Should I use other react lifecycle method?
No, it won't help. Local storage change is not observed, can't be a reason of rerendering.
What is the reason of storing user data in local storage? Sharing data between components shouldn't be the [primary] one.
Components are rerendered on props or state changes. You can pass some value (f.e. isLogged or simply user) from parent state (where probably the login process is handled). If login is processed in sibling child that you need lifting state up - one of basic react techniques.

Why doesn't React automatically allow child components to call setState of the parent component?

I am trying a child component to setState to the parent component, but it not working in reactjs.
React, and the Flux pattern (that helps manage complex application with multiple React components) espouse uni-directional data flow. Uni-directional data flow makes an application easy to reason, and debug because:
If there is something wrong with a React component, you trace upwards to the parent recursively until you find the root cause, because the data flow/setState could have only come upstream
Child React components do indirectly setState of parent but only through notifying parents about certain events, e.g., a mouse click. The parent itself then decides if the mouse click should trigger setState of its own state, which is passed down as props to its children.
class ParentComponent extends React.Component {
onClick() {
const _childValue = this.state.childValue;
this.setState({ childValue: _childValue + 1 });
}
render() {
return (<ChildComponent value={this.state.childVvalue} onClick={this.onClick.bind(this)} />)
}
}
class ChildComponent extends React.Component {
render() {
return (
<span>
<div>My value: {this.props.value} </div>
<button onClick={this.props.onClick}>Increment</button>
</span>
)
}
}

How to force dom re render in react

I am trying to force a child component to re-render. I have tried this.forceUpdate();, but it does not work. I put console.log statements in my <PostList /> component, and none of them are ever called--not componentDidMount, nor componentWillMount, componentWillReceiveProps, none of them. It's as if the <PostList /> component is never initialized. I am sure it is though, because I know for a fact items.count retrieves my items. Here is my render method:
render() {
const items = this.state.posts;
const postList = items.count > 0 ? (<PostList comingFromSearch={true} xyz={items} />) : (<div></div>)
const navBar = <NavigationBar />
return (
<div><br/>{navBar}
<div className="container">
<h3>Search Results for {this.state.searchTerm}</h3>
<div className="row">
<div className="col-x-12">{postList}</div>
</div>
</div>
</div>
)
}
And here is my api call:
retrieveSearch(term) {
Helpers.searchWithTerm(term).then((terms) => {
const postsWithTermsInTitle = terms.titleResults
this.setState({posts: postsWithTermsInTitle})
this.forceUpdate();
}).catch((error) => {
console.log("error searching: " + error);
})
}
I should note, on my previous page, i had another ` component, and maybe react is using that one instead of this one? I want to force it to use this instance.
If this.forceUpdate(); does not make the whole DOM re-render, how can I do that?
thanks
your PostList and NavigationBar Components might not update because they only update when their props are changed (shallow compare).
PostList might not update when changing the inner content of the array, because the component will shallow compare the new state with the previous one. Shallow comparing an array will basically checked against its length property. which does not change in this case.
Quick Solution
Sometimes you need to update a List, without changing any of its props or the length of the list. To achieve this, just pass a prop to the component and keep incrementing it instead of calling force update.
retrieveSearch(term) {
Helpers.searchWithTerm(term).then((terms) => {
const postsWithTermsInTitle = terms.titleResults
this.setState((curState) => ({posts: postsWithTermsInTitle, refreshCycle: curState.refreshCycle+1}))
this.forceUpdate();
}).catch((error) => {
console.log("error searching: " + error);
})
}
render() {
...
<PostList
...
refreshCycle={this.state.refreshCycle}
/>
...
}
Right solution
The right solution is to provide an itemRenderer which you is a function that returns the an individual item from the list. This function is passed as a prop to the component.
This way you have control over how the items inside the list will appear, also changes inside the itemRenderer function will cause a component update.
itemRenderer(itemIndex) {
return <div>{this.props.item[itemIndex]}</div>;
}
render() {
...
<PostList
itemRenderer={this.itemRenderer.bind(this)}
itemsLength={items.length}
/>
...
}
The itemRenderer will be called inside the PostList in a loop (of length itemsLength). each loop will be passed the index of the current iteration, so you can know which item of the list to return from the function.
This way you can also make your list more scalable and more accommodating.
You can check an implementation of such solution on a list package like this one: https://www.npmjs.com/package/react-list
You can force a re-render of a component and all its children by changing the state of the component. In the constructor add a state object:
constructor(props) {
super(props)
this.state = {
someComponentState: 'someValue'
}
}
Now whenever you do:
this.setState(someComponentState, 'newValue')
It will re-render the component and all its children.
This of course assumes your component is a class based component, not a functional component. However, if your component is a functional component you can easily transform it to a class based component as follows:
class ComponentName {
constructor() {
// constructor code
}
render() {
// render code
}
}
export default ComponentName
Understand that componenet level state is not the same as redux state but is exposed only inside the component itself.

How can I find all nested Components using React/Redux?

I am looking to validate a form with Redux. I am trying to use make a form component which will iterate through children and find various input components (not to be confused with a native <input>.
I know there are a lot of open source solutions, but I'd like to understand some mechanics before jumping into picking any. I have a Form component setup to test like this:
import React from 'react';
export default class Component extends React.Component {
componentDidMount() {
this._iterate(this.props.children);
}
render(){
return (
<form {...this.props}>{this.props.children}</form>
);
}
_iterate(children) {
React.Children.forEach(children, child => {
console.log(child);
if (child.props.children) {
console.log('get children');
this._iterate(child.props.children);
}
});
}
};
I then have another Component with a render like this:
render() {
return (
<div>
<Form>
<ComponentA />
<ComponentB />
</Form>
</div>
);
}
Now ComponentA or ComponentB might have a component that nests more components down the line. Within those components would be a React component I have made for Text, Select, etc.
The code above would just console.log the components, and any children of them, that are in this specific render. It does not jump down into ComponentA children.
Is there a solution to that?
This isn't a problem you really want to solve.
The power in react is largely around the design pattern it encourages, and what you're doing is breaking that pattern; Component's should only talk to their immediate children and respond to their immediate parents. If you need to go deeper than that, then the component in the middle needs to be responsible for passing that data.
Rather than trying to dig into the innards of ComponentA and ComponentB, those component's themselves should have the accessibility props that you need. I.e., <ComponentA onChange={whatever} errorMessage={whatever}/> etc. and then hooking those props to their children should occur within ComponentA.

ReactJS - What Alternatives are there to ContextType?

In keeping with the one purpose only principle of components, I have a deeply layered component, that many depths below has a open modal button.
When first completing the entire page, I foolishly realized that having the function to open the modal at a very deep level child, caused the modal window to open within that child's parameters and not the window as a whole.
When trying to overcome this, I found myself, too liberally passing down prop functions to sub components that had no need for that prop save to be passed down even further. I can see how this would be a nightmare down the road.
As a last resort I opted for context type:
Main Page Component:
export default class MainPage extends Component {
......
getChildContext() {
return {
openModal: (arg) => this.openModal(arg)
}
}
openModal(content) {
if (content !== undefined) {
const open = content.modalOpen
this.setState({modalOpen: open});
}
}
render() {
.....
return(
{ modalOpen ?
<Modal>
....
</Modal>
: '' }
)
}
}
Many generations down child component:
export default Child extends Component {
....
static contextTypes = {
openModal: PropTypes.func
};
render() {
.....
<img src="/images/shared/zoom_in.png"
onClick={ this.context.openModal.bind(this, { modalOpen: true }) }
....
/>
.....
}
...
}
One reads often where contextType is discouraged and may be done away with in the future, but in this scenario, I cannot see a cleaner, more efficient approach.
Under the scenario I discussed, am I following a prudent approach or might there be a better way?
Thanks!
I would have my child view dispatch an event that can be handled on in the root component.
Webpack and Browserify both have convenient ways to use a nodejs like event emitter in the browser. This is also what most flux implementations use for their dispatcher.
You could export a singleton instance of an event emitter that you require in both child and parent. ie..
import {EventEmitter} from 'events'
exports default new EventEmitter()
then from your child component you can
import emitter from './youemitter'
....
openModal() {
emitter.dispatchEvent(OPEN_MODAL, props)
}
and in your parent you can just
import emitter from './youremitter'
....
componentDidMount() {
emitter.addEventListener(OPEN_MODAL, this.openYourModal)
In the same way you can listen to events from your child views to update your data model or flux store or what you may have and rerender.

Resources