React native child not receiving updated props value - reactjs

I am new to react and making a little app to learn the library and way of thinking.
But I ran into a problem which has me completely stomped...
I have the root components that contains a list of tasks.
I pass these tasks to a custom component to render them into a groups like so:
<TasksView tasks={this.state.tasks}></TasksView>
Where this.state.tasks is an array containing all the current tasks.
But the problem is that my TaskView component never seems to receive these tasks...
See below for the complete code:
ROOT:
class App extends Component<{}, IAppState> {
constructor(props: any) {
super(props);
this.state = {
taskCategories: [],
tasks: [],
currentTask: {}
};
}
testing() {
this.setState((prev: IAppState) => {
return {
tasks: [...prev.tasks, {
index: 0,
timespan: [TimeTypes.Day, TimeTypes.Week, TimeTypes.Month, TimeTypes.Year][Math.round(Math.random() * 9)],
name: '',
repeatAmount: 0,
repeatDelay: 0,
category: {
id: 0,
color: "",
name: ""
},
onComplete: function (index: number): void {
throw new Error('Function not implemented.');
}
}]
}
});
}
render() {
console.log("tasks count in parent: ", this.state.tasks.length); //<-- console.log A
return (
<SafeAreaView style={styles.container}>
<TasksView tasks={this.state.tasks}></TasksView>
<TouchableOpacity
onPress={this.testing.bind(this)}
style={styles.createTask}
>
</TouchableOpacity>
</SafeAreaView>
)
}
};
TASKSVIEW:
class TasksView extends Component<TasksViewProps, any> {
taskGroups: TasksGroupData[];
stickyIndices: number[];
constructor(props: TasksViewProps) {
super(props);
console.log("props should be updated..."); //<-- console.log B
console.log(props.tasks.length); //<-- console.log C
this.taskGroups = [];
this.stickyIndices = [];
}
render(): ReactNode {
return [
<Text style={tasksViewStyle.title}>Your Goals</Text>,
<ScrollView
stickyHeaderIndices={this.stickyIndices}
showsVerticalScrollIndicator={false}
style={tasksViewStyle.scrollView}
>
{this.taskGroups.map((group: TasksGroupData, index: number) =>
<TasksGroup key={index} timespan={group.timespan} tasks={group.tasks}></TasksGroup>
)}
</ScrollView>
];
}
}
I have left out all the interface definitions and some helper functions since they would not be relevant to the problem at hand.
So what i would expect is every time console.log A gets executed console.log B and C would also be executed but this is not the case.
See here a screenshot of current console.log sequence.
If any additional information is required let me know so I can update the question.

The constructor is only run once when your component is first mounted, because React re-uses the same class instance for the lifetime of the component (see these docs). This is why you're not seeing your logging calls after they are initially run once.
For logging that runs on each render, you could move your console.logs into componentDidUpdate.

Related

How to update state into Provider context in class in React

i have a problemn, i need to pass an array to class DragAndDropProvider but not working, i can see value in console log but not is possible update state with value, received error as:
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
moment in that i call the class
Index
const documents = UseGetDocuments();
const { state, setItemsArray } = useContext(DragAndDropContext);
setItemsArray(documents);
DragAndDropProvider
export class DragAndDropProvider extends Component<any, any> {
constructor(props: any) {
super(props);
this.state = {
items: [],
moveItem: this.moveItem,
setItems: this.setItems
};
}
an_independent_function(documents: any) {
console.log(documents);
this.setState({ items: documents })
}
render() {
return (
<DragAndDropContext.Provider
value={
{
state: this.state,
setItemsArray: (documents: any) => {
this.an_independent_function(documents);
},
}
}>
{this.props.children}
</DragAndDropContext.Provider>
);
}
When using Class components you need to bind 'this' for it to work in your callback function.
constructor(props: any) {
...
this.an_independent_function = this.an_independent_function.bind(this);
}

Getting updated state for save in react native [duplicate]

I'm quite new to react native and i didn't found any solution to my problem yet so I'm asking here now. I'm trying to update the state of a value on an onPress event of a radio button and save it afterwards. The problem is that the save is getting the non updated value. I know setState is an async call and a forceUpdate is not a recommended solution(and don't work for me for some reason)
here is a sample:
import RadioForm, {
RadioButton,
RadioButtonInput,
RadioButtonLabel
} from 'react-native-simple-radio-button'
class SomeClass extends Component {
constructor(props) {
super(props)
this.state = {
buttonValues: [{label: "someValue1", value: 0}, {label: "someValue2", value: 1}],
someString: "someStringValue_false"
}
this.handleOnPress = this.handleOnPress.bind(this),
this.saveValue = this.saveValue.bind(this)
}
handleOnPress(value) {
if( value === 1 ){
this.setState({
someString: "someStringValue_true"
})
} else {
this.setState({
someString: "someStringValue_false"
})
}
}
saveValue() {
//no problem in this function tested already in other cases
}
render() {
return(
<View>
<RadioForm
radio_props={this.state.buttonValues}
initial={0}
formHorizontal={true}
labelHorizontal={true}
radioStyle={{paddingRight: 20}}
buttonColor={"red"}
selectedButtonColor = {"green"}
animation={true}
onPress={(value) => this.handleOnPress(value)}
/>
<Button
title={"save"}
onPress={()=> this.saveValue()}
/>
</View>
)
}
}
Behavior: the state updates only on the 2nd call
you can try yo use setState callback
setState({ name: "Michael" }, () => console.log(this.state));
// => { name: "Michael" }
to make sure that the state changes.
You are not binding the handlers with this properly in the constructor. It should be
constructor(props) {
/* --- code --- */
this.handleOnPress = this.handleOnPress.bind(this),
this.saveValue = this.saveValue.bind(this)
}
Do the next.
this.setState({
meter_reading_unit: "someStringValue_false"
}, () => {
console.log('meter_reading_unit ==> ', this.state.meter_reading_unit); // should be `someStringValue_false`
});
inside your next render update the value someStringValue_false should be available inside anywhere.

setState does not cause children to update

I'm building a fairly simple application with react, socket.io, and react-apexcharts (or any other charting library for that matter).
I'm pretty sure i understood the concept of states well enough - I'm not new to programming, but, i can't seem to understand what the problem is.
I have a simple react component:
export default class NumberCandleStickChart extends Component {
constructor(props) {
super(props);
this.state = {
options: {
chart: {
id: "number-candlestick",
},
},
series: props.series,
a: props.a
};
}
render(){
return (
<>
<p>{JSON.stringify(this.state.a)}</p>
<Chart
options={this.state.options}
series={this.state.series}
type="candlestick"
width="500"
/>
</>
);
}
}
And it is used like that:
//imports
class App extends Component {
constructor(props) {
super(props);
this.socket = openSocket('http://localhost:4200');
this.state = {
connected: false,
interval: '10s',
series: [{
name: "numbers",
data: []
}]
}
this.handleNumberIn = this.handleNumberIn.bind(this);
this.socket.on('newNumber', this.handleNumberIn)
}
handleNumberIn(input) {
const newSeries = [...this.state.series];
input.data.map(//map to correct format and push to newSeries);
this.setState({
series: newSeries
});
}
render() {
return (
<div className="App">
<header className="App-header">
<p>
{this.state.connected.toString()}
</p>
<NumberCandleStickChart series={this.state.series} a={this.state.series} />
</header>
</div>
);
}
}
Now, a prop is there to let me see if the state actually changes, and it does, it adds data to the array correctly, according to the docs.
I tried emulating the same with setInterval, and the same happens, and i tried a different charts library, and still no luck - so i am assuming i am doing something not right.
App was created using the create-react-app boilerplate - nothing more.
Any help is appreciated!
Your problem is in handleNumberIn. It should be:
handleNumberIn(input) {
this.setState(prevState => ({
series: [...prevState.series, input.data.map(/* your map function */)],
}));
}
Specifically, Array.prototype.map() returns a new array with the mapped data, it does not modify the existing one.
In the constructor method of NumberCandleStickChart , the lines
series: props.series
a: props.a
are invoked only once.Whenever the state in App.js updates ,the new props received by NumberCandleStickChart are not mapped to its state again.
You do not need the this.state.series in NumberCandleStickChart.You could directly use
<Chart
options={this.state.options}
series={this.props.series}
type="candlestick"
width="500"
/>
You could do the same for this.state.a

Why is my react state data undefined in react?

I am creating a react 360 application using an API to fetch data and then display it on a panel. Below I have the following code:
export class CryptoControlInformation extends React.Component {
state = {
details: {
description: '',
links: [],
}
};
componentDidMount() {
const CRYPTO_CONTROL_PATH = 'https://cryptocontrol.io/api/v1/public/details/coin/bitcoin?key=some_key';
fetch(CRYPTO_CONTROL_PATH)
.then(response => response.json())
.then(data => {this.setState({
details: {
description: data["description"],
links: [...this.state.details.links, ...data["links"] ]
}})
})
}
render() {
let links = this.state.details.links;
##################################
console.log(links[0])
{_id: "5b41d21fa8a741cf4187ec60", type: "reddit", name: "r/Bitcoin Reddit", link: "https://reddit.com/r/Bitcoin/"}
####################################
####################################
// Why is this returning undefined?
console.log(links[0]["name"]);
####################################
return (
<View>
<View style={styles.rightHeader}>
<Text style={{fontSize: 40}}>Information</Text>
</View>
<View>
<Text>{this.state.details.description}</Text>
</View>
<View>
<Text>{this.state.details.description}</Text>
</View>
</View>
)
}
}
I can't get the information inside my object and I don't understand why. I know that the information is there. I can console.log the object in its entirety but the individual pieces are undefined. What am I doing wrong? I've noticed in react that the state always has to be explicitly detailed.
For example, I found that I can't just do this:
export class Square extends React.Component {
state = {
info: undefined
};
componentDidMount() {
// grab information (pseudocode)
this.setState{info: data}
}
}
I have to actually map out the data which is annoying:
export class Square extends React.Component {
state = {
info: {
color: '',
height: '',
width: '',
}
};
componentDidMount() {
// grab information (pseudocode)
this.setState{info: {
color: data['red'],
heigth: data['height'],
width: data['width']
}
}
}
}
I'm thinking that this has something to do with my problem. Am I on the right track?
Standard timing problem - you didn't look for 'react undefined', right?
When component loads data render is called (minimum) twice - once at initial mounting (w/o data) and 2nd time when data arrives (setState forces new render)
console.log (in chrome) cheats you silently updating earlier message
you can use map - it works fine with initially empty array - or check if value is ready in jsx
{!links.length && <Text>{links[0]["name"]}</Text/>}
... conditionally call rendering function, return <Loading /> earlier, etc.
Using setState with function (Varun's comment) isn't required, it's safer (for some cases) but not obligatory

React child component can't get props.object

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>
);
}
}

Resources