I am using React and Redux in a project I’ve two components a parent and a child.
The parent component passes some props to the child when the child component receives those props it calls some action which change some of the props (async) that the parent passed to its child. Now my redux-store shows that the data has successfully stored in it. But my child component doesn’t re-render itself.
Parent Component:
class Parent extends React.Component {
getChilds(){
let child = [];
let i = 0;
for (let keys in this.props.home.data) {
child[i] = (
<Child title={keys} data={this.props.home.data[keys]} key={keys} />
);
i++;
if (i === 6) break;
}
return Rows;
}
render(){
return (
<div>
<h1>I am gonna call my child </h1>
{this.getChilds()}
</div>)
}
}
Child Component:
class Child extends React.Component {
// Here I'm not sure which lifecycle method to use to i'm gonna use
// componentDidMount
componentDidMount(){
if(this.props.data.items.length === 0){
// calling an action to fill this.props.data.items array with data
this.props.getData(this.props.data.id);
}
}
getGrandSong(){
let grandSons = [];
if(this.props.data.items.length > 0){
grandSons = this.props.data.items.map( item => <GrandSon item={item}
/>);
}
return grandSons;
}
render(){
return (
<div>
<h1> I am the child component and I will call my own child </h1>
{this.getGrandSon()}
</div>
)
}
Props are updated when I check the props in react-dev tool.
You need the data to re-render whenever the prop changes, so you can call it on the render function and implement shouldComponentUpdate() to deeply compare props and call render().
So, in this case:
class Child extends React.Component {
componentDidMount(){
if(this.props.data.items.length === 0){
// calling an action to fill this.props.data.items array with data
this.props.getData(this.props.data.id);
}
}
shouldComponentUpdate(nextProps){
if(this.props.data.items.length === nextProps.data.items.length){
for(let i=0; i< nextProps.data.items ; i ++){
if(nextProps.data.items[i] !== this.props.data.items[i]) return true;
}
}
return true;
}
render(){
const getGrandSong = () => {
let grandSons = [];
if(this.props.data.items.length > 0){
grandSons = this.props.data.items.map( item => <GrandSon item={item} />);
}
return grandSons;
}
return (
<div>
<h1> I am the child component and I will call my own child </h1>
{getGrandSon()}
</div>
)
}
Related
I am trying to render a list of components in order with react, the component is updating this array of elements but is not re-ordering them.
Pseudo code;
class Form extends Component {
//
// .... other initialization code and logic
//
updatePositions() {
//
// re-order this.state.page.page_contents
//
this.setState({ page: this.state.page });
}
renderContents() {
return this.state.page.page_content.map((c, i) => {
return (<ContentItem
key={ i }
content={ c }
/>);
});
}
render() {
return (
<div className="row">
<div className="medium-12 columns">
{ this.renderContents() }
</div>
</div>
);
}
}
If i log out the results of page.page_content the items are being reordered in the array, however the form render is not re-rendering the contents in its new order
You shouldn't be using array indices as keys if your array order is subject to change. Keys should be permanent, because React uses the keys to identify the components, and if a component receives a key that previously belonged to a different component, React thinks it is the same component as before.
Create unique keys for your elements that are permanent to those elements.
you could try to force update
renderContents() {
this.forceUpdate();
return this.state.page.page_content.map((c, i) => {
return (<ContentItem
key={ i }
content={ c }
/>);
});
}
Don't mutate this.state, directly. Bring it into a new variable and then add it back into state.
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
from: https://reactjs.org/docs/react-component.html
Instead, you should try:
updatePositions() {
const page_contents = [...this.state.page.page_contents]
// re order page_contents
this.setState({ page: { page_contents });
}
renderContents() {
return this.state.page.page_content.map((c, i) => {
return (<ContentItem
key={ i }
content={ c }
/>);
});
}
it's your code here - key={i} i is not changing so it will not re-render the component - if you want to re-render the component - please make sure that - key should change.
renderContents() {
return this.state.page.page_content.map(c => {
return (<ContentItem
key={ c }
content={ c }
/>);
});
}
c is content - if it's change then Component will re-render
this.setState({ page: this.state.page }) it's wrong - ur trying to set the same value in same variable again .
class Form extends Component {
//
// .... other initialization code and logic
//
updatePositions() {
//
// re-order this.state.page.page_contents
//
this.setState({ page: newValueFromAPI.page });
}
render() {
const { page: { page_content } } = this.state
return (
<div className="row">
<div className="medium-12 columns">
{ page_content.length > 0 && (
page_content.map(c => <ContentItem key={c} content={c}/>)
)}
</div>
</div>
);
}
}
I am trying to build a portal where I have 3 components.
Parent component
->Child 1
->Child 2
From main component
-if (no files are selected) then GUI should show just the Child 1 and
Count and pathnames of files associated with Application
-else if (user has clicked on any file) then GUI should show the filename and
nodes associated to it.
I am trying to achieve this but i am confused in how to pass info from parent to child and vice versa.
In the code given below in Child 1.js when user click on path, the Parent component should update the GUI view by calling Child2 rather than calling Child1 .
How can i achieve this?
For update parent component state from child component with arguments. You need to create method in parent component, that set state from arguments from this method. And pass this method to child component by props.
class Parent extends React.Component {
state = {text: ""}
updateText = text => {
this.setState({text: text})
}
render () {
return (<Child updateText={this.updateText}>)
}
}
class Child extends React.Component {
render () {
return (
<button
onClick={
() => this.props.updateText("updated state from child component")
}
>Update State</button>
)
}
}
Building on what galishmann provided, just pass y.Filename to the function in props as it already expects a vlaue as a parameter.
class Parent extends React.Component {
state = { text: "" }
updateText = text => {
this.setState({ text: text })
}
render() {
return (<Child updateText={this.updateText} />)
}
}
class Child extends React.Component {
....
....
....
render() {
const { updateText } = this.props;
const pathElement = this.state.groupedByAppName.map((x) => {
return (
<Collapsible trigger={x.AppName + '\t' + x.Count + ' files'} transitionTime={20}>
{
x.section.map((y) => <p filename={y.FileName} onClick={() => updateText(y.Filename)}>{y.Path}</p>)
}
</Collapsible>
)
})
return <div> {pathElement} </div>
}
}
I would like to update all child components in an array using a prop passed down from the state of a parent component. A basic example is shown below. Each child component in the array is stateless and has a prop value which is determined by the state of the parent component. However, when the parent components state changes, the child components do not re-render with the change. How can I make the child components re-render when the parents state changes? Thanks!
import React from 'react';
import ReactDOM from 'react-dom';
class Child extends React.Component {
render(){
return (
<p>
<button onClick = {(e) => this.props.onClick(e)}>
click me
</button>
{this.props.test}
</p>
)
};
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {msg: 'hello', child_array: []};
this.handleClick = this.handleClick.bind(this);
}
handleClick(e){
e.preventDefault();
const msg = this.state.msg == 'hello' ? 'bye' : 'hello';
this.setState({msg: msg});
}
componentDidMount(){
let store = [];
for (var i = 0; i < 5; i++){
store.push(<Child test = {this.state.msg} key = {i} onClick = {this.handleClick}/>);
}
this.setState({child_array: store});
}
render(){
return(
<div>
{this.state.child_array}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
As mentioned in comments
It won’t re render again because you are generating chil components in componentDidMount and this method gets called only once per the component after first render. So when your callback fires the child_array will be empty
Instead what you can do is remove componentDidMount method code and do that in render as like below. In the below case it will render every time the onclick fires in child component
render(){
const store = [];
for (var i = 0; i < 5; i++){
store.push(<Child test = {this.state.msg} key = {i} onClick = {this.handleClick}/>);
}
return(
<div>
{store}
</div>
)
The problem is that you're rendering the child components (and thus baking in the value of this.state.msg) in the parent's componentDidMount() method. You need to render them in the parent's render() method instead.
componentWillReceiveProps will work in your case, everytime you will get props child component will re-render.
class Child extends React.Component {
constructor(props) {
super(props);
let initialData = (
<p>
<button onClick = {(e) => self.props.onClick(e)}>
click me
</button>
{nextProps.test}
</p>
);
this.state = {data: initialData };
}
componentWillReceiveProps(nextProps) {
let self = this;
let updatedHtml = (
<p>
<button onClick = {(e) => self.props.onClick(e)}>
click me
</button>
{nextProps.test}
</p>
);
this.setState({data: updatedHtml})
}
render(){
return (
{data}
)
};
}
So, I have a class like this:
class Blah extends Component {
constructor(props) {
super(props);
}
handleComponent = (event) => {
let divid = event.target.getAttribute('id');
if (divid === 'col') {
// I want to render component by this condition
} else if (divid === 'ro') {
// or I want to render component by this condition
} else {
//or I want to render component by this condition
}
};
render() {
const { classes } = this.props;
return (
<div>
<div id = 'col' onClick={this.handleComponent}>Sheep</div>
<div id = 'ro' onClick={this.handleComponent}>Cow</div>
<div id = 'ball' onClick={this.handleComponent}>Dog</div>
{ I want to render my component here after click }
</div>
);
}
}
I have another class written on top of this:
class Flow extends Component {
constructor(props){
super(props);
};
render() {
return(
<div style={{background:'somecolor'...blah blah}}>Clap</div>
);
}
}
And I am Passing this by:
var foo = withStyles(styles)(Flow)
I have tried returning components but I am not getting anywhere.
I can use ternary operator but it still will render only one of two but I have three component have three design for each of them.
I want to render one of them to render on some condition as stated above.
If I use states that for toggle that will too have two components for render. Don't go on the code, this is made up, So any Ideas ? Fragments ? Any help will be appreciated. Thank you.
To render component by condition simply use switch statement.
In my example we use state to store current active component.
renderMyComponent method takes care of rendering one of three possible components.
handleChange method changes current state and triggers new render of App component.
This example use class properties plugin.
renderMyComponent = () => {
means autobind and is the same as using in constuctor method
this.renderMyComponent = this.renderMyComponent.bind(this);
Working example:
const ComponentOne = () => <div>Hi, i am component one</div>;
const ComponentTwo = () => <div>Hi, i am component two</div>;
const ComponentThree = () => <div>Hi, i am component three</div>;
class App extends React.Component {
state = { current: 0 }
renderMyComponent = () => {
// Our switch conditional render
switch(this.state.current) {
case 0:
return <ComponentOne />;
case 1:
return <ComponentTwo />;
case 2:
return <ComponentThree />;
default:
return null;
}
}
handleChange = (event) => {
// We are looking for data-trigger attribute
// In this example we expect type number but trigger holds string
// That's why we 'cast' to a number using Number()
const current = Number(event.target.dataset.trigger);
// Sets new state of current component and triggers new render
this.setState({ current })
}
render() {
return (
<div>
<div>
Pick component to render
<button
type="button"
data-trigger="0"
onClick={this.handleChange}
>
Render 1
</button>
<button
type="button"
data-trigger="1"
onClick={this.handleChange}
>
Render 2
</button>
<button
type="button"
data-trigger="2"
onClick={this.handleChange}
>
Render 3
</button>
</div>
{this.renderMyComponent()}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Reviewing your code:
You don't need constructor here.
...
constructor(props){
super(props);
}
...
Callback componentDidMount works perfectly when I am dealing with elements in that component, but I want to execute a function that deals with element of multiple components on a page, after a group of components been mounted. What is the best way I can do that?
You could pass a callback function into each component using props that each componentDidMount method calls to their parent component, which say, updates a counter in that component's state. When the counter reaches the amount of components you expect to render, execute your function.
In Parent component:
....
shouldComponentUpdate(nextProps, nextState) {
return nextState.childCount >= 4;
}
incrementChildCount() {
this.setState({ childCount: this.state.childCount + 1 });
}
render() {
return(
<div id="container">
<Child callme={this.incrementChildCount} />
<Child callme={this.incrementChildCount} />
<Child callme={this.incrementChildCount} />
<Child callme={this.incrementChildCount} />
);
}
In Child component:
componentDidMount() {
this.props.callme()
}
If the other components are all children, you could define a function for them and assign them a ref when instantiating. Then the parent can execute that function:
...
componentDidMount: function() {
var ret = true;
var refs = this.refs;
for (var ref in refs) {
if (refs.hasOwnProperty(ref)) {
ret = refs[ref].isValid().success && ret;
}
}
return ret;
},
render: function() {
return (
<div>
<ChildComponent ref="something" />
<ChildComponent ref="somethingElse" />
</div>
);
}
The child would have something like:
...
isValid: function() {
// this is just an example; i don't know what you want your children to return or do...
ret = true;
if (<you conditions here>) {
ret = false;
}
return ret;
},
...