I'm trying to update child component which has a prop with parent state. While changing state of parent with setState() to my knowlage chlid should rerender. But when child is in an array rerender does not occure. How can i fix that?
Thanks in advance.
My child class:
class Child extends Component {
render() {
return(
<Text>{this.props.state.isActive.toString()}</Text>
)
}
}
My parent class:
class Parent extends Component {
state = {
isActive: false
}
children = []
constructor(props) {
super(props)
this.createChildren()
this.changeState()
}
changeState() {
this.change = setInterval(() => {
if(this.state.isActive){
this.setState({isActive: false})
} else {
this.setState({isActive: true})
}
}, 1000)
}
componentWillUnmount() {
clearInterval(this.change)
}
createChildren() {
for(let i=0; i<5; i++) {
this.children.push(<Child key={i} state={this.state}/>)
}
}
render() {
return(
<View>
{this.children}
</View>
)
}
}
My app function:
export default function App() {
return (
<View style={style.conteiner}>
<Parent/>
</View>
);
}
React elements should always be created during render. Do not store them in a property or in component state:
renderChildren = () => {
children = [];
for(let i=0; i<5; i++) {
children.push(<Child key={i} state={this.state}/>)
}
return children;
}
render() {
return(
<View>
{this.renderChildren()}
</View>
)
}
You are basically rendering always the same elements that you created once in the constructor (which also is an anti-pattern).
What you are doing is not going to work, since you are pushing <Child/> components to a class attribute which is only called inside the constructor; so these <Child/> components will refer to only initial state, not the now updated state. You will have to call a getChildren() function inside the render. I've fixed the issue in react, you can translate to react native replacing the divs with Views, see codesandbox : https://codesandbox.io/s/pedantic-jackson-wgtnh?file=/src/App.js
Related
I am making a front end application using typescript and react. I have a component A which amongst other html elements has a textbox. I want to add this component A on click of a button. So if the user clicks the button multiple times, i want a new component A to be created on every click. Also I want to be able to store the text data so that I can later fetch it and process it.
I tried to make a list of this component but it gives me an error.
interface State {
componentList?: ComponentA[];
}
export class ComponentList extends React.Component<Props, State> {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
public onClick(event) {
const componentList = this.state.componentList;
this.setState({
componentList: componentList.concat(<ComponentA key=
{componentList.length} />)
});
}
public render() {
return (
<React.Fragment>
<button onClick={this.onClick}>Add component</button>
{this.state.componentList.map(function(component, index)
{
return ComponentA
})}
</React.Fragment>
);
}
}
You might want to make two changes in your code.
First initialise your state in the constructor,
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = { componentList: [] }
}
So that react can track that data.
Second thing is, you are returning wrong item from the map in the render function.
Try returning component, which is different copies of <ComponentA ../> that you pushed every time you clicked the button,
public render() {
return (
<React.Fragment>
<button onClick={this.onClick}>Add component</button>
{this.state.componentList.map(function(component, index)
{
return component;
})}
</React.Fragment>
);
}
Keep the component count in the state:
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = {
numComponents: 0
}
}
Add a new function which creates an array of the component for rendering later:
clickedComponents = () => {
let componentArray = [];
for (let i=0; i<this.state.numComponents; i++) {
componentArrays.push(<ComponentA />);
}
return componentArray;
}
Increment the component count with your onClick function:
public onClick(event) {
this.setState({numComponents: this.state.numComponents + 1});
}
Render the component array:
public render() {
return (
<React.Fragment>
<button onClick={this.onClick}>Add component</button>
{this.clickedComponents()}
</React.Fragment>
);
}
I am trying pass value to my child components. The value that I am getting is coming from the an API that I called in my parent component and being called in the componentDidMount but the problem is the child components is not reading the props I am passing in his own componentDidMount, its only getting blank even in the reactdevtool it passing correct values. I solved this before but cannot remember what I did can you help. Thanks
Child:
componentDidMount() {
const {
events
} = this.props;
console.log(events)
}
Parent:
class App extends Component {
componentDidMount() {
let self = this;
GetAllMainItems().then(function(GetAllMainItemsResults) {
let MainObject = self.state.MainObject;
self.setState({
GetAllMainItemsResults
});
}
}
render() {
constructor() {
super();
this.state = {
MainObject: []
};
}
return ( <
div className = "App row" >
<
Calendar events = {
this.state.MainObject
}
/>
<
/div>
);
}
There are a few things you need to review.
constructor should be outside of render method.
You do not have to use let self = this. you can just do this.setState({...}) there.
Look at your GetAllMainItems callback. I don't know what you get
there. but you are definitely not setting mainObject in your state.
Instead, you will have this.state.GetAllMainItemsResults.
Recommendations
Try to understand object destructuring.
Use arrow functions
Hope it helps.
Parent Component
```
class App extends Component {
state = {
mainObject: ""
};
componentDidMount() {
GetAllMainItems().then(response => {
this.setState({
mainObject: response
});
});
}
render() {
const { mainObject } = this.state;
return (
<div className="App row">
<Calendar events={mainObject} />
</div>
);
}
}
The problem you are having is that your child component is re-rendering when it receives new events props.
Try adding a componentDidUpdate method to see these props updating:
componentDidUpdate(prevProps, prevState) {
console.log(prevProps, prevState);
console.log('events:', prevProps.events, this.props.events);
}
I created a component wrapper around ViewPagerAndroid (simplified version)
class TabView extends Component {
constructor(props){
super(props)
this.state = { position: 0 }
}
changePage = (key) => {
this._pagerRef.setPage(key)
this.setState({position: key})
}
render(){
return(
<ViewPagerAndroid ref={(ref) => this._pagerRef = ref}>
{ this.props.scenes }
</ViewPagerAndroid>
)
}
}
I want to trigger changePage from outside the component (eg from: <TabView ref={(ref) => this._ref = ref} />, and run this._ref.changePage(key)).
However, each time I try to do so, this._pagerRef is undefined inside the changePage function of TabView.
What am I missing ?
There is a more idiomatic React solution to the problem you are trying to solve -- namely making TabView a controlled component and setting ViewPager page on componentDidUpdate:
class TabView extends Component {
componentDidUpdate = ({ page }) => {
// call setPage if page has changed
if (page !== this.props.page && this._pagerRef) {
this._pagerRef.setPage(page);
}
};
render() {
return (
<ViewPagerAndroid
initialPage={this.props.page}
ref={ref => this._pagerRef = ref}
onPageSelected={e => this.props.pageChanged(e.nativeEvent.position)}
>
{this.props.scenes}
</ViewPagerAndroid>
);
}
}
You can then move the current page tracking to the parent component's state and pass it down to TabView as a prop, along with a handler that updates it when the value changes:
render() {
return (
<TabView
page={this.state.page}
pageChanged={page => this.setState({page})}
/>
)
}
You're trying to access the ref from outside of the component which has no instance to it.
Therefore you need to pass it as a prop from the parent component itself. Also you need to move the changePage to the parent component to access it from outside.
Parent
changePage = (key) => { //... Call the function here
this._pagerRef.setPage(key)
this.setState({position: key})
}
accessRef (ref) {
this._pagerRef = ref . //... Bind the ref here
}
<TabView passRef={this.accessRef} /> //...Pass the ref here
Child
<ViewPagerAndroid ref={this.props.passRef}> . // ... Set the ref here
{ this.props.scenes }
</ViewPagerAndroid>
I'm using the react to build some input forms.
While all children inputs have and their own states to store values I have no idea how to process the to a parent.
Here's example:
class FormComponent extends Component {
constructor(props) {
super(props);
this.state = {
title: null,
someAmount: null
}
}
render() {
let me = this;
return (
<div>
<TextField
value={me.state.title}
onChange={(proxy, value) => {
me.setState({title: value})
me.hanleChnage();
}
}
/>
<TextField
value={Number.parseFloat(me.state.someAmount)}
onChange={(proxy, value) => {
if (!isNaN(Number.parseFloat(value))) {
me.setState({someAmount: value})
me.hanleChnage();
}
}
}
/>
</div>
)
}
handleChange() {
//Calling the parent
//State here is outdated
this.props.onUpdate && this.props.onUpdate(this.state);
}
}
export default FormComponent;
Or where I can find some example of usage of compex forms with much inputs in react.
Thanks!
Sounds like you need to consider moving some of your state into the parent components. The React docs have a good article about this.
To summarize, you can pass your hanleChnage(); function as a prop to your child components if you declare the function in your parent.
function handleChange() { //do something... }
...
<ChildComponent parentOnChange={this.handleChange.bind(this) />
As your components grow in complexity, you might consider using Redux for state management, thus serving as a single source for all state in your application.
Set a child property, (e.g. callParentProperty) to reference a function in the parent component (e.g. parentFunction).
class ParentComponent extends Component{
parentFunction(parameter) {
console.log("This is the form value");
console.log(parameter);
}
render() {
return <FormComponent callParentFunctionProperty={this.parentFunction.bind(this)} />
}
}
class FormComponent extends Component {
...
handleChange() {
...
let formValue = this.state.someAmount;
this.props.callParentFunctionProperty(formValue);
}
}
I'm new to React and I have a question about sharing properties from one component to another. For example, I want a parent component that has a "visible" function that I can pass to other child components.
Example:
CustomInput visible="true";
CustomDropDown visible="false"
I'd like to know the best way to do this, respecting good practices. Thank you for your help!
Real simple. You can pass methods as props. Suppose you have a parent, or Higher Order Component (HOC), you could do something like this:
class Parent extends React.Component {
logWord = (word) => {
console.log(word);
}
render () {
return <ChildComponent handleLogging={ this.logWord } />
}
}
Then, in the ChildComponent, you simply access the method from props. For instance:
class ChildComponent extends React.Component {
render () {
return (
<div onClick={ this.props.handleLog.bind(null, 'Logged!') }>Click me to log a word!</div>
}
}
}
So, in your example, if you wanted a method that existed on the parent that updated a visibility attribute on your state, you could write:
class Parent extends React.Component {
constructor () {
this.state = {
visible: false
}
}
setVisible = (bool) => {
this.setState({ visible: bool });
}
render () {
return <ChildComponent updateVisible={ this.setVisible } visible={ this.state.visible } />;
}
}
ChildComponent:
class ChildComponent extends React.Component {
render () {
return (
<div>
<div onClick={ this.props.updateVisible.bind(null, true) }>Set me to visible!</div>
<div onClick={ this.props.updateVisible.bind(null, false) }>Set me to invisible!</div>
{ this.props.visible && <div>I'm visible right now!</div> }
</div>
}
}
}