React Native ref not called on dynamic component - react-native

I have a component that dynamically renders different components based off of props. I am unable to set the ref on one of them. This question had a duplicate ref, which I do not have. In addition, the components are built inside the render function.
Here is my code:
render() {
let content = null
switch(this.props.type) {
case 'follow'
content = <View ref={comp => this._followBtn = comp} />
break
// ...etc
}
return (
<View ref={comp => (this._wrapper = comp)>
{ content }
</View>
)
}
I need access to _followBtn from a parent component rendering the _wrapper.
I have only accomplished accessing _wrapper.
Does my problem stem from the fact that these are dynamically generated?
The ref prop never seems to be called.

Related

Retrieve state values from many of the same child component react native

I have a screen that contains many of the same CustomSlider component. I would like to retrieve the slider values from every slider.
What is best practice for doing this in react native?
Here's a minimum working example, with 3 sliders:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import MultiSlider from "#ptomasroos/react-native-multi-slider";
class CustomSlider extends Component {
constructor(props) {
super(props)
this.state = {
multiSliderValue: [1, 9]
}
}
multiSliderValuesChange = values => {
this.setState({multiSliderValue: values});
};
render(){
return (
<MultiSlider
values={this.state.multiSliderValue}
onValuesChange={this.multiSliderValuesChange}
min={0}
max={10}
step={1}
/>
)
}
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
get_slider_values = () => {
// what is best practice to access the values of every slider here?
// eg an object like this
const slider_values = [[1.4, 7.4], [4.3, 7.0], [1.9, 3.2]]
return slider_values
}
render() {
return (
<View style={{alignItems: 'center', justifyContent: 'center', padding: 50}}>
<CustomSlider />
<CustomSlider />
<CustomSlider />
<Text>{`The slider values are: ` + JSON.stringify(this.get_slider_values())}</Text>
</View>
);
}
}
There is no need for a complex solution. The way that I would handle this is to manage the state in the parent component. The CustomSlider doesn't really need to know its state. As the parent component needs to know the state of the sliders it is better to handle it there.
So as the parent component is going to handle the state this means we need to make some changes to what you are doing.
Set initial values in the parent component for the state of each of the sliders. This is important, it makes it means that even if the user doesn't touch the sliders we know the values of them.
Pass a function to each of the sliders that calls back to the parent component.
As the parent component is controlling the state we can remove the state from the CustomSlider. This gives a few options we could leave it as a Component, change it to a PureComponent or go one step further an change it to a Functional Component If the slider doesn't really need to know its state then the last option should be best for performance.
Here is how I would refactor your App.js
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
sliderValues: [[1, 9],[1, 9],[1, 9]] // we should control the state here
};
}
// this uses function currying to bind the function and pass a value to it
onChange = (index) => (values) => {
this.setState( prevState => {
let sliderValues = prevState.sliderValues;
sliderValues[index] = values;
return {
sliderValues
}
})
}
render() {
return (
<View style={{alignItems: 'center', justifyContent: 'center', padding: 50}}>
<CustomSlider intialValues={this.state.sliderValues[0]} onChange={this.onChange(0)}/>
<CustomSlider intialValues={this.state.sliderValues[1]} onChange={this.onChange(1)}/>
<CustomSlider intialValues={this.state.sliderValues[2]} onChange={this.onChange(2)}/>
<Text>{`The slider values are: ` + JSON.stringify(this.state.sliderValues)}</Text>
</View>
);
}
}
Notice how we don't actually need a function to get the values of the sliders as they are stored in state. That means we can access the sliders' values directly by using this.state.sliderValues.
Here is your CustomComponent refactored to work with the above code:
class CustomSlider extends Component { // this could easily be swapped for a PureComponent
render(){
return (
<MultiSlider
values={this.props.intialValues}
onValuesChange={this.props.onChange}
min={0}
max={10}
step={1}
/>
)
}
Notice how it doesn't need to manage state at all as the parent component is handling it. It also means that we can remove a lot of code that isn't actually necessary. This is why I think we can go one step further and make it a Functional Component
const CustomSlider = ({intialValues, onChange}) => {
return (
<MultiSlider
values={intialValues}
onValuesChange={onChange}
min={0}
max={10}
step={1}
/>
)
}
If however if the CustomSlider needs to know its state because it is doing something more than capturing the values of the slider then you can easily add state to it by using it as a Component or a PureComponent.
Snack
Here is a snack showing the above code working. I have shown all three possible components and have used them in the App.js. There isn't much difference in how they look, but your use case will determine which one that you use. https://snack.expo.io/#andypandy/multisliders
Best Practice
The best practice is to go for the simplest solution that you can find. Ideally that would be a Functional Component, then a PureComponent, and finally a Component. It is also important to think about where and how the state is going to be used. Some questions that I ask myself are:
Does a component really need to know its own state?
Where do I plan on using that state?
How long do I need these state values for?
Do I need to persist that state?
What tools are available to me based on what I am currently using?
Do I really need to add another dependency or more to make this work?
If you need the values from the sliders in multiple places in your app you can use some of the features that are provided by react-native or your navigation to pass these values around. Redux and MobX are big overheads in terms of complexity and should only really be used if you need a global state management system, for the majority of cases they can be avoided.
You can store the state dynamically by some key given to each child, and access each ones state by the key you give it.
One way is to pass a closure from parent component to CustomSliders as props and monitor the changes.
<CustomSlider idx={n}
theClosurePassedThrough= (n, values) => {
// update the parents states here accordingly
}
>
Then call this closure at appropriate time.
multiSliderValuesChange = values => {
this.setState({multiSliderValue: values});
this.props.theClosurePassedThrough(this.props.idx, values);
};
The best practice, though, is to use MobX or Redux.

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

dynamic text change in react-native

Hi I am looking for the solution to change text dynamically.
I am writing code to show processing results on screen.
After some googling, I found there is a code to update text dynamically as follows.
But I would like to update text without any internal event. I want to change text from outside of the class. But I don't know how to implement it as I am a javascript and react-native beginner. There are other classes to process some functions so that I need to show the updated results using Results class which is an another component of the screen.
How can I deliver 'result' to Results class and how to update it dynamically and automatically?
class Results extends Component {
constructor() {
super()
this.state = {
log: 'Processing results'
}
}
updateText = (result) => {
this.setState({log: result})
}
render() {
return (
<View>
<Text onPress = {this.updateText}>
{this.state.log}
</Text>
</View>
)
}
}
This sounds to me that props can solve your problem.
Basically when you try to render Results class, pass along the value as a prop like below:
<Results dynamicText='HI' />
Then, from your Results class, access this external value via this.props.dynamicText as below
class Results extends Component {
render() {
return (
<View>
<Text>
{this.props.dynamicText}
</Text>
</View>
)
}
}
In addition to what #Issac answered, you can also hook up your current class to Redux and dispatch actions from another class to force state changes.
React Native and ReactJS has a different concept of how classes react to each other. Most other languages use inheritance based interactions to affect changes in classes other than itself. React itself is more composition based where changing the value/state/variable of one class requires either a state change or a prop change. The caveat to that us using Redux, which utilizes an overarching Store where any component that's connected to it can pull values or dispatch actions to change values.

Using FlatList#onViewableItemsChanged to call a Component function

I'm currently attempting to implement a form of LazyLoading using the FlatList component, which introduces a neat little feature called onViewableItemsChanged which gives you a list of all of the components that are no longer on the screen as well as items that are now on the screen.
This is a custom LazyLoad implementation and as such is more complicated than most LazyLoad open-sourced libraries that are available, which is why I'm working on my own implementation. I'm already looked into react-native-lazy-load and others.
Basically, I need to be able to call a function that's part of the component being rendered in the FlatList, I've tried creating a reference to the item rendered in the FlatList and calling it as such, but it doesn't seem to work.
For example:
<FlatList data={...}
renderItem={(item) => <Example ref={(ref) => this[`swiperRef_${item.key}`] = ref}}
onViewableItemsChanged={this.onViewableItemsChanged}
/>
onViewableItemsChanged = ({viewableItems}) => {
viewableItems.forEach((item) => {
const { isViewable, key } = item;
if(isViewable && !this.cachedKeys.includes(key)) {
const ref = this[`swiperRef_${key}`];
if(!ref) return console.error('Ref not found');
ref.startLoading();
this.cachedKeys.push(key);
}
});
}
Now in the <Example /> component I would have a function called startLoading which should be called when a new visible item is brought onto the screen, however the ref never exists.
I was actually doing everything correctly, but I accidently forgot to deconstruct the parameter returned from the renderItem function, so (item) should have been ({ item })
That's all there was to it.

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