Lifting state up does not work in React native - react-native

I probably misunderstood something very basic here.
I want to run a method in my child component whenever the state "oranges" in my parent component changes.
My Child component looks like this:
const [someText, setSomeText] = useState(props.oranges);
const consoleThis= () => {
if (someText == true){
console.log("win!")
}else{
return
}
};
useEffect(() => {
consoleThis();
}, [someText]);
This is the code in my parent component
...
const [apples, setApples] = useState(false);
<child
oranges ={apples}
/>
...
<Pressable
onPress={() => {
setApples(true);
}
>
Any ideas why I don't get my "win!" console logged?

useState(initial) doesn't reset the state each time the value passed to it changes - it is only used for setting the initial state of the value. Passing a new prop value will not update the state variable value - the state value becomes its own copy of the initial value that is managed independently of the prop value. In general, when you're lifting the state up, you don't want to also be keeping state in your child component. Instead, just drive the child component off of the state value passed via props.
You probably want something that responds to changes in the orange prop, like this:
function ChildComponent({orange}) {
useEffect(() => {
if (orange) {
console.log("win!")
}
}, [orange])
};

Related

How to load "dynamic" value from store to TextInput on loading and handle its onChangeText?

I need to get value from store in redux to TextInput. I know it can be set by value={this.props.Value}. I need to save that value to save later, so I change it by using local state like this
value={this.state.Value}
onChangeText={text => {
this.setState({
Value: text
})
}}
And in constructor of component, I set the value of TextInput to local state
constructor(props) {
super(props);
this.state = {
Value: this.props.Value
}
}
This work fine. But now, that Value is generated on loading this component.
componentDidMount() {
this.props.onLoadView();
}
onLoadView() will dispatch an action that generate new data of Value in store. But constructor function is called before componentDidMount(), that mean component state Value is initial data in store, not the new one. If I change it back to value={this.props.Value} to fix this problem, I can not handle that value to save later.
I know I can change onChangeText to update the value of TextInput to store immediately. But whenever I do not want to save this value, I have to change it back to the initial value. Is there any easier way to solve this problem?
Thank you!
As said in the comment, you can use componentDidUpdate to check your previous props and comparing them to the ones you are using in your component:
componentDidUpdate = () => {
if(this.props.Value !== this.state.Value) this.setState({Value : this.props.Value })
}
As you are storing the previous props.Value in your state you can directly compare the current props with the current state

Why I can't read a state in react native?

I have the following code:
export default class Testing extends Component {
state = ({
data: []
});
componentDidMount() {
this.setState({
data: this.props.values
});
console.log(this.state.posts); //prints empty but if I do
console.log(this.props.values); //prints the array correctly
}
Where is the error since I can print the props not the state?
Thanks
You're not storing anything in this.state.posts. Your initial state only contains data.
Also when you construct your initial state you should do it like this:
state = {
data: []
}
You do not need the ( ) around it.
If you are wanting to print a value from state as soon as you have stored it you must use the callback functionality of state. This is due to the fact that setState is asynchronous and takes time to set the value. Currently you are trying to read the value before it has been set, use the callback functionality like below.
this.setState({
data: this.props.values
}, () => console.log(this.state.data));
Here are some great articles on setState.
https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0
https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
https://medium.learnreact.com/setstate-takes-a-function-56eb940f84b6
you don't need the ( ) when you set the initial state because it is an object.
export default class Testing extends Component {
state = { //remove (
data: []
}; //remove )
Also worth noting, setState is an async function. You will not be able to getState directly after setState.
In order to get the state right away, you would provide a callback to setState() https://reactjs.org/docs/react-component.html#setstate

How to update attribute value by using ref in react native?

In below code there is "opened" attribute and I want to change its value by using ref. Here I am using ref as indexed array.
<Menu renderer={renderers.SlideInMenu} ref={(Menu) => { this.rowRefs[item.id] = Menu; }} opened={false}>
I tried it as
function updateRef(id){
React.findDOMNode(this.refs.id).setAttribute("opened", true);
}
Can anyone please explain how to create an indexed reference and how to use it?
Props should be immutable and for the purpose of dynamically change update them you should consider to set them via state.
Your code should look like:
<Menu renderer={renderers.SlideInMenu} ref={component => this.menuRef = component }} opened={this.state.opened}>
In which case the <Menu .. > is assumed to be rendered in a component which has a state variable opened which you can change using this.setState({opened: true}) . This state change will make your UI rerender hence <Menu .. > will be rendered with opened={true}.
Also if you want to use ref, then you should consider making a state variable inside Menu which should be initialized with opened prop, and you should have a method in the Menu which will change the state.
Your code should look like below:
class Menu extends React.Component {
constructor (props) {
super(props);
this.state = {
menuOpened: props.opened
}
}
changeMenuOpened = (value) => {
this.setState({
menuOpened: value
})
}
.....
}
and then you can just call the changeMenuOpened method using Menu's ref from the parent.
this.menuRef.changeMenuOpened(true);

React-Native how to update dropdown

I'm using react-native-chooser to create my dropdowns and when I select 1 item from dropddown1 I want to update the items from dropdown2. Thanks.
In react, to make your UI change, you need to update your state. From looking at the docs for react-native-chooser it has a callback method called onSelect. Here, the currently selected option is returned to you to use. Based on this selected option you can update the state of second dropdown. The important part here is the parent child relationship. In react, a child is only re-rendered if the parent's state is updated (unless otherwise specified). Some pseudocode:
// Your method callback
onSelect = (option) => {
const newOptions = computeNewOptions(option)
this.setState({options: newOptions})
}
// Your Second dropdown component would take these options in as a prop
render () {
return (
<SecondDropDown options={this.state.options} />
)
}
// You can then access your options through the props
export default class SecondDropDown extends React.Component {
render () {
let myOptions = renderOptions(this.props.options)
return (
<View>
{myOptions}
</View>
)
}
}

React Native + Redux: How to subscribe to changes in state?

I have a component and I want to call a method checking the state whenever it changes. This is my component with a dummy method to demonstrate what I want to do (animate the view offscreen if onboarding.show === false):
export class Onboarding extends Component {
animateView() {
// i want to call this method when
// the state changes
// something like;
if (!this.props.onboarding.show) {
Animated.spring(...);
}
}
render() {
const { onboarding, finish } = this.props;
return (
<Animated.View>
...
</Animated.View>
);
}
}
...
export default connect(
state => {
return {
onboarding: state.onboarding,
};
},
dispatch => {
return {
};
}
)(Onboarding);
Is there a way to subscribe to the changes in state?
== UPDATE ==
as requested, here's what my slideOffScreen method does:
slideOffScreen() {
Animated.timing(this.state.offsetX, {
toValue: -Dimensions.get('window').width,
duration: 350,
easing: Easing.elastic(),
}).start();
}
The react-redux connect method wraps the component with a container component that is aware of the store's state changes. Whenever the state changes, connect re-renders the wrapped component (Onboarding in your case).
According to the redux docs:
Technically, a container component is just a React component that uses
store.subscribe() to read a part of the Redux state tree and supply
props to a presentational component it renders. You could write a
container component by hand, but we suggest instead generating
container components with the React Redux library's connect()
function, which provides many useful optimizations to prevent
unnecessary re-renders.
If your component doesn't re-rendered when the state changes, check if you're not mutating the state instead of replacing it. Redux checks if the state changed by shallowly comparing the old state, and the new state (comparing only the references, and not the values).
For example, to add an item to an array, you can't use array.push(item) because that won't create a new array, just mutate the existing one. Instead you'll have to use something like array.concat(item), which does.
To update objects, you can see in the redux docs under handling actios example, you can see that to create a new state:
We don't mutate the state. We create a copy with Object.assign().
Object.assign(state, { visibilityFilter: action.filter }) is also
wrong: it will mutate the first argument. You must supply an empty
object as the first parameter. You can also enable the object spread
operator proposal to write { ...state, ...newState } instead.
Looks like this works:
componentWillReceiveProps(props) {
if (!props.onboarding.show) {
this.slideOffScreen();
}
}
not sure if there's a way to do it through the redux API