I have ChooseLevel component which has many buttons. Once I click a button onLevelSelected function is being called and it call action creator which will return an action type, moreover it will redirect me to qPage component. In qPage component I make some calculation and I put the result in total variable.
Here's my code:
ChooseLevel.js
class ChooseLevel extends Component {
constructor(props) {
super(props);
}
onLevelSelected(levelNumber) {
this.props.levelSelected(levelNumber);
}
render(){
<Button key={1} onPress={this.onLevelSelected.bind(this, 1)}>
<Button key={2} onPress={this.onLevelSelected.bind(this, 2)}>
<Button key={3} onPress={this.onLevelSelected.bind(this, 3)}>
....
}
}
My action creator looks like this:
import { Actions } from 'react-native-router-flux';
export const levelSelected = (levelNumber) => {
return (dispatch) => {
dispatch({
type: 'level_selected',
payload: levelNumber
});
Actions.qPage(); // redirects me to qPage component
};
};
qPage.js
class qPage extends Component {
constructor(props){
...
}
calc(){
.... some calculation
total = result of previous calculation
}
render(){
....
....
}
}
How do I pass total variable from qPage to ChooseLevel page?
What you need to do is, i will go step by step:
1) create one more action and pass calculated value inside that action.
2) access that calculated value through props(reducer)
3) You can use componentWillUpdateProp(nextprops), Inside this fuction assign this new prop to the state of the class.
Cheers :)
Related
hi everyone i am recently learning react native and i have a synchronization problem sending data from one screen to another screen.
I have the parent page (UserList) that wants to send its status to the child page (RecyclerViewPage) but the render is called before the data is available.
export default class UserList extends Component {
constructor(props){
super(props);
this.state={
comment_state: [],
datetime: []
}
}
//getPost is a network call which gets and store the result in the state of the class
async getPost(){
var sid=this.props.route.params.value_sid
var did=this.props.route.params.value_did
const utils=new Utils();
const responseJson = await utils.getPost(sid,did)
const comment = (responseJson?.posts ?? []).map((data) => data.comment)
this.setState({comment_state:comment})
console.log("now i change state with new value")
}
componentDidMount(){
this.getPost()
}
render(){
return(
<RecyclerViewPage
comment={this.state.comment_state}
/>
)
}
}
and RecyclerViewPage code:
export default class RecyclerViewPage extends Component {
constructor(props) {
super(props);
console.log("i am in recyclerviewPage : ",this.props.comment)
}
render(){}
}
I tried to put some logs and the result is:
Log: "i am in recyclerviewPage: []"
Log: "now i change state with new value"
It appears as if render () was called before the asynchronous getPost method has finished. how can i synchronize my code in order to get the data first and then send it to the RecyclerViewPage daughter schemata?
See you can wait for the results and then show recyclerviewpage:
You can add a loader state while its being fetched:)
Hope it helps. feel free for doubts
export default class UserList extends Component {
constructor(props){
super(props);
this.state={
comment_state: [],
datetime: []
}
}
//getPost is a network call which gets and store the result in the state of the class
async getPost(){
var sid=this.props.route.params.value_sid
var did=this.props.route.params.value_did
const utils=new Utils();
const responseJson = await utils.getPost(sid,did)
const comment = (responseJson?.posts ?? []).map((data) => data.comment)
this.setState({comment_state:comment})
console.log("now i change state with new value")
}
componentDidMount(){
this.getPost()
}
render(){
return(
{!!comment_state && <RecyclerViewPage
comment={this.state.comment_state}
/> }
{!comment_state && <Text> Please wait its loading </Text>}
)
}
}
I would like to know how to manage state property when the component mounts and unmounts.
I have a lot of different components in my application to maintain the application flow. I know about function componentdidmount and componentWillUnmount. and I also tried the solution about _isMounted=true on componentdidmount function and check _isMounted properties value when I update setState and then update _isMounted=false on componentWillUnmount function.
but this won't work when more two components come in the picture.
For example following links:
https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component/
Is there a way to check if the react component is unmounted?
as per the example, I have made a common class which will update the value of a component in setMounted function and will return value in getMounted function to validate component is mounted or not. These methods work correctly on a single screen when I call another screen from a stack and update some values then comes back on the previous page and refresh page it will ismount=false.
class Mount {
isMounted=false;
getMounted=()=>{
return isMounted;
}
setMounted=mounted=>{
isMounted=mounted;
}
}
var mount=new Mount();
export default mount;
class example extends component{
componentDidMount=async()=>{
mount.setMounted(true);
await this.loadScreen();
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
async() => {
await this.loadScreen();
}
);
}
loadScreen=async()=>{
//some other stuff
if(mount.getMounted()){//second time value is false
this.setState({value:'value'});
}
}
componentWillUnmount() {
mount.setMounted(false);
}
//renderview where i call example2 on buttonclick
}
class example2 extends component{
componentDidMount=async()=>{
mount.setMounted(true);
await this.loadScreen();
}
loadScreen=async()=>{
//some other stuff
if(mount.getMounted()){
this.setState({value:'value'});
this.props.navigation.goBack();
}
}
componentWillUnmount() {
mount.setMounted(false);
this.willFocusSubscription.remove();
}
}
It was showing following warning before using mount functions:
Can't perform a React state update on an unmounted component
You are creating only a single instance of your Mount class that is exported and shared across every instance of every component. You will need to create a new instance of Mount for each component instance:
class Mount {
...
}
// export the Mount class
export default Mount;
class example extends component{
constructor(props) {
super(props);
// create an instance of Mount for each component instance
this.mount = new Mount();
}
componentDidMount=async()=>{
this.mount.setMounted(true);
await this.loadScreen();
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
async() => {
await this.loadScreen();
}
);
}
loadScreen=async()=>{
//some other stuff
if(this.mount.getMounted()){//second time value is false
this.setState({value:'value'});
}
}
componentWillUnmount() {
this.mount.setMounted(false);
}
//renderview where i call example2 on buttonclick
}
Notice the addition of the constructor and the use of this.mount instead of mount throughout.
I have a static method in my component as follows:
static updatestate(totalCount,totalCost){
this.setState({totalCount,totalCost});
}
I only have to make the static method to pass data to the component.but as you know we can't use this in the static methods. Is there any way to pass data to a non-static method, without creating a new instance and without creating props. as following:
import MyComponentName from './MyComponentName.js';
MyComponentName.MyMethod(params)
Thanks.
As you state, you can't use this in static methods.
If you are trying to invoke a method on a specific component instance, use a ref to that instance. Suppose you wanted to invoke updateState on this component:
class ChildComponent extends React.Component {
state = {totalCount: 0, totalCost: 0};
updateState = (totalCount, totalCost) => {
this.setState({totalCount, totalCost});
}
render() {
const {totalCount, totalCost} = this.state;
return <Text>totalCount: {totalCount} totalCost: {totalCost}</Text>;
}
}
You would get a ref to an instance, and can then call updateState:
class ParentComponent extends React.Component {
render() {
return (
<View>
<ChildComponent ref={child => {
this.child = child; // gets a ref to the child component
}} />
<TouchableOpacity onPress={() => {
const {totalCount, totalCost} = this.child.state;
this.child.updateState(totalCount + 1, totalCost + 10); // call method on child component
}}><Text>press</Text></TouchableOpacity>
</View>
);
}
}
(If instead, you want to setState on all instances of a component, you might be better offer using a React.createContext Provider that the ChildComponent's could be a Consumer of).
I'm new on React-Native and it's my first React-Native app. However, I have already some problems.
I want to pass a variable from one class (Home.js) to an another. (Is it possible without using the composent in the render() fonction ?)
##### Home.js #####
class Home extends Component {
constructor(props) {
super(props);
this.state = {direction: "defaultvalue"};
}
getCurrentDirection() {
return this.state.direction;
}
render() {
/***..... some elements ..*/
}
}
export default Home
And
#### Two.js ####
import Home from './Home'
/** SOME CODE **/
const DrawerOptions = {
initialRouteName: Home.getCurrentDirection(),
contentComponent: CustomDrawerContentComponent,
drawerWidth: 300,
};
However it doesn't work... How to resolve it ? I have already try some solutions as declare the getCurrentDirection as static but nothing.
In addition, it seems to be a specific case because DrawerOptions is not a class. Could you please, add to your response also, how make it if I want to obtain the variable into the class Two.js ?
I meant if Two.js was for example :
##### Two.js #####
class Two extends Component {
var myvariable = Home.getCurrentDirection();
render() {
/***..... some elements ..*/
}
}
Thanks a lot in advance
A recommendable way of accessing the state from a component into another is to use (in this case) the Home component as a parent of Two component. This way you don't have to trigger a function to access the Home's state. On each time when the state of the parent (in this case) component will be updated, the Two component will receive the updated property (direction). If you want to call a function from Two component, you have to pass it a function as a property (changeCurrentDirection) that will call back the function you want to trigger from Home component.
So you would have something like this:
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
direction: "defaultValue"
};
}
changeCurrentDirection() {
this.setState({
direction: "valueChanged"
})
}
render() {
let state = this.state;
return (
<Two
direction={state.direction}
changeCurrentDirection={() => this.changeCurrentDirection.bind(this)}/>
)
}
}
class Two extends React.Component {
render() {
let props = this.props;
return (
<div>
<h3>{props.direction}</h3>
<button onClick={props.changeCurrentDirection()}>Change value</button>
</div>
)
}
}
React.render(<Home/> , document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script>
<div id="app"></div>
Additional info you can find here.
Also, if you want to have a good management of the state of your components, my advice for you is to use redux. Using this library you can easily connect the component's actions and properties that can further be accessible from other files where you can manage them.
My code is
const main = () => {
let caption;
AsyncStorage.getItem("XXX", (err, result) => {
caption = <View>...</View>
});
render (
...
{caption}
...
);
}
But I got an error as below.
RawText "" must be wrapped in an explicit <Text> component.
I'm going to assume that, based on your pseudo-code, you understand how to get data from AsyncStorage, that it's not a good idea to be using AsyncStorage inside your render function, and that you don't actually mean ajax but rather local storage.
But the error is showing up because you need to make sure you wrap text inside a <Text> element. If you look at this paragraph it says:
In React Native, we are more strict about it: you must wrap all the text nodes inside of a <Text> component; you cannot have a text node directly under a <View>.
EDIT:
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: '',
};
}
componentDidMount() {
AsyncStorage.getItem('XXX', (err, result) => {
// #TODO: You should handle errors too
this.setState({
data: result.text,
});
});
}
render() {
// Returning null will not render anything
// Once the results come in, it will update automatically
if (!this.state.data) return null;
// Raw text must be wrapped in Text
return (
<Text>{this.state.data}</Text>
);
}
}
You can try to stock data in a state and display your component whit a function:
AsyncStorage.getItem("XXX", (err, result) => {
this.setState({result:result})
});
function element(){
if([check if your state is not null])
return(
<View>... {this.state.result} ...</View>
)
}
render (
...
{this.element()}
...
);