How can I make the child component gets an updated context from the parent? I am passing ReactDOM.findDOMNode(this.refs['something']).offsetWidth and I want the child to get the updated value when the width changes.
You need to bind to onresize event. Something like the code below should do the trick.
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {offsetWidth: this.offsetWidth()};
window.addEventListener('resize', this.onResize.bind(this));
}
offsetWidth() {
return ReactDOM.findDOMNode(this.refs['something']).offsetWidth;
}
onResize() {
this.setState({offsetWidth: this.offsetWidth()});
}
render() {
const {offsetWidth} = this.state;
return (
<Child offsetWidth={offsetWidth}/>
);
}
}
Related
I am quite new with React and I have problem bellow
I have a parent component like this:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {count:1};
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return false;
}
setCount = () => {
this.setState({
count: 2
});
};
render() {
const {name, running, onRun, onStop} = this.props;
return (
<div>
<Test count={this.state.count}/>
<p><a href="#" onClick={this.setCount}>SetCount</a></p>
</div>
);
}
}
And here is Test component
class Test extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return true;
}
render() {
const {count} = this.props;
return (
<div>
{console.log("Counting")}
<p>{count}</p>
</div>
);
}
}
I have method "shouldComponentUpdate" returns "false" in Parent component because I don't want to re-render it.
My understanding is React know which part of DOM need to be re-rendered. And in this case, the state of Parent changes will re-render "Test" component
But when I run above code, "Test" component does not redender.
Is there anything wrong in my code?
Thanks a lot for your help
You need to return true from your parent's shouldComponentUpdate method.
If you return false, after the initial render it won't update, even if you call a function that calls setState.
Is the refresh of the whole page are you talking about? If thats the case, probably you wanna change your <a> tag to button or use e.preventDefault();.
If not, I am not sure if that is possible. If you setState in the parent, it will rerender parent as well as the children. If you dont want to render the parent then you have to manage individual state management in the child level.
For example,
class Parent extends React.Component {
constructor(props) {
super(props);
}
render() {
const {name, running, onRun, onStop} = this.props;
return (
<div>
<Test/>
</div>
);
}
}
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {count:1};
}
setCount = (e) => {
e.preventDefault();
this.setState({
count: 2
});
};
render() {
const {count} = this.state;
return (
<div>
{console.log("Counting")}
<p>{count}</p>
<p><a href="#" onClick={this.setCount}>SetCount</a></p>
</div>
);
}
}
So I have a component called "itemSelection" which contains a state with a property called "allItems" of type array
constructor(props){
super(props);
this.state = {
allItems: []
}
}
Then I have a component called "methods" which contains a function that returns a value
selectMethod = (e) => {
const x = e.target.getAttribute("data");
this.setState({method: x}, () => console.log(this.state.method));
}
So, What I want to do is to bring the value of propery "method" and push it to the "allItems" array aside with its current state.
The solution is to lift state up: the shared state (here the items) should be kept by the closest ancestor of both components. This ancestor then passes the state to the children via props, along with a callback function that children can use to mutate the state.
For instance in pseudo-code:
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
allItems: []
}
this.onSelect = this.onSelect.bind(this)
}
onSelect(item) {
this.setState({allItems: this.state.allItems.push(item)})
}
render() {
return (
<Child1 items={this.state.allItems}/>
<Child2 onSelect={this.onSelect}/>
)
}
}
class Child1 extends React.Component {
render() {
return (
{this.props.items.map(i => i.name)}
)
}
}
class Child2 extends React.Component {
render() {
return (
<button onClick={this.props.onSelect(...)}>button</button>
)
}
}
In the itemSelection component, create a function that will update allItems. Then, pass that function in a property to the method component and call it from there.
I am trying to render an parent-component which has two children. The rendering of the children will switch, so one time there will be only the first child rendered, another time it will be the last and finally it will switch back to the first child (which then should contain all the values shown before).
I thought this would be simple but it turned out that it is not.
Now to the problem: Whenever the method switchContainer is called, it will switch the container and render the other. However all member-variables, props and states are getting lost and it basically reinstanciated the child-component from scratch.
Is there a way to save the child-components "as-is" and once it is getting re-rendered, it will hold all the member-variables, props and states again?
I know that you can send props and states to the element like this:
<Child props={<data>} states={<data>}>
but this doesn't solve the issue with the missing membervariables and in my opinion it isn't a smooth solution.
My attempt so far is (this is just a mockup):
class Parent extends React.Component<any,any> {
private firstContainer:any;
private secondContainer:any;
private currentContainer:any;
constructor(props:any) {
super(props);
this.firstContainer = <Child>;
this.secondContainer = <Child>;
}
public render() {
return (
<div>
{this.currentContainer}
</div>
);
}
public switchContainer() {
if(this.currentContainer === this.firstContainer) {
this.currentContainer = this.secondContainer;
}
else {
this.currentContainer = this.firstContainer;
}
this.forceUpdate();
}
}
class Child extends React.Component<any,any> {
private anyValue:string;
constructor(props) {
this.change = this.change.bind(this);
}
public render() {
return (
<input onChange={this.change} value={this.anyValue}/>
);
}
private change(e:any) {
this.anyValue = e.target.value;
}
}
You can try maintaining a state and update children in render instead of saving child as firstContainer and secondContainer
class Parent extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
firstChild: true
};
}
public render() {
const { firstChild } = this.state;
<div>
<Child1 show={firstChild}/>
<Child2 show={!firstChild} />
</div>
}
public switchContainer() {
this.setState(({ firstChild }) => ({ firstChild: !firstChild }));
};
}
And in child component, handle show to showContent otherwise render null. If you want to retain state, you should not unmount the component.
Im trying to use some Object Pattern in React Components because the usual Component->child structure require often a code rewriting.
class SuperComponentEveryOneWillLove extends React.component
{
constructor(props){
this.state = { master_state_all_will_use : 0 }
this.commonFunction = this.commonFunction.bind(this);
this.getMasterState = this.getMasterState.bind(this);
}
commonFunction() { return do_something; }
getMasterState() { return this.state.master_state_all_will_use }
}
class PoorSon extends SuperComponentEveryOneWillLove
{
constructor(props){
this.state = { for_me_only : 0 }
}
render() {
<span>
{ this.state.master_state_all_will_use } //DOESN'T WORKS
{ this.getMasterState() } //DOESN'T WORKS
{ this.state.for_me_only } //WORKS
{ this.commonFunction() } //WORKS
</span>
}
}
I need to access Parent state and local state.
React allow function super calling but not merging state. I've tried on google to look for some "super" or "parent" keyword to access parent state but it seems it doesn't exists.
At runtime, Son component has no scope of Father's state.
Is this possibile?
I'm not sure if it is what are you looking for, but it's close to it:
class Parent extends React.Component {
constructor(props){
super(props)
this.state = { isParentState: true };
// needed for getting access to parent state
this.getMasterState = this.getMasterState.bind(this)
}
getMasterState(){
return this.state;
}
}
class Enhancer extends Parent {
constructor(props){
super(props);
// get parent state via super keyword
const parentState = super.getMasterState();
this.state = {
isChildState: true,
...parentState
}
}
render() {
return <div>
Merged state: { JSON.stringify(this.state)}
</div>
}
}
Worked example.
Hope it helps
I'm sure it's possible, but you DO NOT want to do this.
Anything you are trying to do with inheritance can be done with composition.
In you case, your "parent component" will pass any information the children may need as props
class Parent extends React.component
{
constructor(props){
this.state = { parentState : 0 }
this.parentFunction= this.commonFunction.bind(this);
}
parentFunction() { console.log("parentFunction()"); }
render() {
return (
<Child
parentFunction={parentFuction}
parentState={this.parentState}
/>
)
}
}
https://reactjs.org/docs/composition-vs-inheritance.html
EDIT
In React, inheritance is almost NEVER the answer.
Now if you're looking for a a way to reuse method logic, why not abstract the method to a helper file?
If that still doesn't work, perhaps a Higher Order Component (HOC) will do the trick.
Here's an example of a simple HOC:
const withCommonFunction = (WrappedComponent) => {
return class extends React.Component {
commonFunction() {
console.log("I'm a common function that is needed in many components!");
}
render() {
return (
<WrappedComponent commonFunction={this.props.commonFunction} />
);
}
}
}
Then you wrap whichever component you want to have the same logic with the HOC.
const Child = withCommenFunction(Child);
This is typically used to help reuse logic that would otherwise be implemented the same in different components
My parent component is like this:
export default class MobileCompo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
datasets: {}
};
this.get_data = this.get_data.bind(this);
}
componentWillMount() {
this.get_data();
}
async get_data() {
const ret = post_api_and_return_data();
const content={};
ret.result.gsm.forEach((val, index) => {
content[val.city].push()
});
this.setState({data: ret.result.gsm, datasets: content});
}
render() {
console.log(this.state)
// I can see the value of `datasets` object
return (
<div>
<TableElement dict={d} content={this.state.data} />
<BubbleGraph maindata={this.state.datasets} labels="something"/>
</div>
)
}
}
child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
console.log(this.props);
// here I can't get this.props.maindata,it's always null,but I can get labels.It's confusing me!
}
componentWillMount() {
sortDict(this.props.maindata).forEach((val, index) => {
let tmpModel = {
label: '',
data: null
};
this.state.finalData.datasets.push(tmpModel)
});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}
I tried many times,but still don't work,I thought the reason is about await/async,but TableElement works well,also BubbleGraph can get labels.
I also tried to give a constant to datasets but the child component still can't get it.And I used this:
this.setState({ datasets: a});
BubbleGraph works.So I can't set two states at async method?
It is weird,am I missing something?
Any help would be great appreciate!
Add componentWillReceiveProps inside child componenet, and check do you get data.
componentWillReceiveProps(newProps)
{
console.log(newProps.maindata)
}
If yes, the reason is constructor methos is called only one time. On next setState on parent component,componentWillReceiveProps () method of child component receives new props. This method is not called on initial render.
Few Changes in Child component:
*As per DOC, Never mutate state variable directly by this.state.a='' or this.state.a.push(), always use setState to update the state values.
*use componentwillrecieveprops it will get called on whenever any change happen to props values, so you can avoid the asyn also, whenever you do the changes in state of parent component all the child component will get the updates values.
Use this child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
}
componentWillReceiveProps(newData) {
let data = sortDict(newData.maindata).map((val, index) => {
return {
label: '',
data: null
};
});
let finalData = JSON.parse(JSON.stringify(this.state.finalData));
finalData.datasets = finalData.datasets.concat(data);
this.setState({finalData});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}