Is there a way to modify the label prop from the gifted-chat component? - react-native

I am using Gifted Chat https://github.com/FaridSafi/react-native-gifted-chat/ but I need some labels to be in another language, namely the LoadEarlier label found here: https://github.com/FaridSafi/react-native-gifted-chat/blob/master/src/LoadEarlier.tsx
defined here:
static defaultProps = {
onLoadEarlier: () => {},
isLoadingEarlier: false,
label: 'Load earlier messages',
containerStyle: {},
wrapperStyle: {},
textStyle: {},
activityIndicatorStyle: {},
activityIndicatorColor: 'white',
activityIndicatorSize: 'small',
}
static propTypes = {
onLoadEarlier: PropTypes.func,
isLoadingEarlier: PropTypes.bool,
label: PropTypes.string,
containerStyle: ViewPropTypes.style,
wrapperStyle: ViewPropTypes.style,
textStyle: PropTypes.any,
activityIndicatorStyle: ViewPropTypes.style,
activityIndicatorColor: PropTypes.string,
activityIndicatorSize: PropTypes.string,
}
I am using the chat like intended by importing the react-native-gifted-chat component and then just using <GiftedChat /> as part of the props it has:
loadEarlier={true}
isLoadingEarlier={false}
I want to know if there's some way I can reach that label to modify the "Load earlier messages" to another language. I can't figure it out.

Looks like you should use the renderLoadEarlier prop to render a <LoadEarlier> button with a custom label.
import it-
import { LoadEarlier } from 'react-native-gifted-chat';
then in render -
render() {
const myLoadEarlier = (props) => <LoadEarlier {...props} label="Custom Load Earlier Label" />;
return(
<View>
...
<GiftedChat
...
renderLoadEarlier={myLoadEarlier}
/>
</View>
);
}
Hope this helps

Related

Is it possible to Get 'style' property values from React Native Element AFTER rendering using useRef?

I'm trying to dynamically apply margin & padding to a View, based on a ref'd TextInput's borderRadius. I am new to React coming from Xamarin where this type of thing is common.
I'm not sure if I have the correct approach, but I have seen some examples of people deriving style values from useRef.
Here is my custom LabelInput component:
import React, {useState} from 'react';
import {
View,
Animated,
StyleSheet,
ViewProps,
} from 'react-native';
import {Colors} from '../../../resources/colors';
import Text from './Text';
import TextInput from './TextInput';
import {TextInputProps} from 'react-native/Libraries/Components/TextInput/TextInput';
import {isNullOrWhiteSpace} from '../../../utils/stringMethods';
import {TextProps} from 'react-native/Libraries/Text/Text';
interface LabeledInputProps {
label: string;
error: string;
onChangeText: (text: string) => void;
placeholder?: string;
inputValue: string;
mask?: (text: string) => string;
validator?: (text: string) => string;
onValidate?: (value: string) => void;
viewProps?: ViewProps;
textProps?: TextProps;
errorTextProps?: TextProps;
inputProps?: TextInputProps;
}
export default function LabeledInput(props: LabeledInputProps) {
const inputRef = React.useRef<any>(null);
const [dynamicStyle, setDynamicStyle] = useState(StyleSheet.create({
dynamicContainer:{
marginHorizonal: 0,
paddingHorizonal: 0,
}
}));
const changeTextHandler = (inputText: string) => {
const displayText = props?.mask ? props.mask(inputText) : inputText;
props.onChangeText(displayText);
// ultimately not the exact behavior I'm after, but this is a simple example.
var test = inputRef.current.props.style;
// props.style always returns undefined,
// there doesn't appear to be a 'props' property on the 'current' object when debugging.
setDynamicStyle(StyleSheet.create({
dynamicContainer:{
marginHorizonal: test.borderRadius, // I want the padding/margin of this element to be
paddingHorizonal: test.borderRadius,// dynamically set based on the inputRef's borderRadius
}
}))
};
return (
<View
{...props.viewProps}
style={[
props.viewProps?.style,
localStyles.container,
]}>
<TextInput
ref={inputRef}
{...props.inputProps}
placeholder={props.placeholder}
style={localStyles.input}
onChangeText={changeTextHandler}
value={props.inputValue}
/>
<Animated.View
pointerEvents={'none'}>
<Text
{...props.textProps}
style={[props.textProps?.style, animatedStyles.label]}>
{props.label}
</Text>
</Animated.View>
{/* {stuff} */}
</View>
);
}
const localStyles = StyleSheet.create({
container: {
backgroundColor: 'blue',
justifyContent: 'flex-start',
flex: 1,
},
label: {
fontWeight: 'bold',
marginBottom: 8,
},
input: {
padding: 8,
},
error: {
backgroundColor: 'pink',
fontSize: 12,
paddingHorizontal: 8,
color: Colors.danger,
marginTop: 4,
},
});
const animatedStyles = StyleSheet.create({
label: {
fontSize: 16,
fontWeight: 'normal',
},
});
Here is my custom LabelInput component with forwardRef() implemented:
import React, {ForwardedRef, forwardRef} from 'react';
import {TextInput as NativeTextInput, TextInputProps} from 'react-native';
import {useGlobalStyles} from '../../../resources/styles';
const TextInput = (
props: TextInputProps,
ref: ForwardedRef<NativeTextInput>,
) => {
const styles = useGlobalStyles();
return (
<NativeTextInput
{...props}
ref={ref}
style={[styles.textInput, props.style]}
placeholderTextColor={styles.textInput.borderColor}
onChangeText={(text: string) => {
if (props.onChangeText) {
props.onChangeText(text);
}
}}
/>
);
};
export default forwardRef(TextInput);
I've tried referencing inputRef from different hooks, like useCallback & useEffect.
var test = inputRef.current.props.style; always returns undefined. And there doesn't appear to be a 'props' property on the 'current' object when debugging.
The link you mentioned contains two files with inputRef. Since inputRef is in parent component and use ref prop to pass inputRef, this will not work. ref is not available as prop. If you still want to use ref as prop, then use forward ref in child component as access the ref as second argument or you can use any other prop name to pass ref i.e. innerRef. You can read more in react documentation. Forward Refs
According to the code you attach in code sandbox, i think you are trying to access input styles in two components: App and LabeledInput. You should use one ref in main component and use it in LabelInput component. If you still want to have separate refs then you can ref callback function and attach the node with both refs.
const attachRef = (node: NativeTextInput) => {
inputRef.current = node;
ref.current = node;
};
return <TextInput ref={attachRef} />;
The correct type for inputRef.current is TextInputProps.
const inputRef = useRef() as MutableRefObject<TextInputProps>;
I have updated the code sandbox. I was able to access input field styles in both components. Hope this solves your problem.

The array for multi select drop down is not referenced well and gives an error

I am trying to use a multi select drop down as explained here but one way or the other the items array is not properly defined.
This is my code for the friendselector component:
/* eslint-disable react-native/no-inline-styles */
import React, {Component} from 'react';
import {View} from 'react-native';
import MultiSelect from 'react-native-multiple-select';
export default class FriendSelector extends Component {
constructor() {
super();
this.state = {
selectedItems: [],
items: [
{
id: '92iijs7yta',
name: 'Kenneth',
},
{
id: 'a0s0a8ssbsd',
name: 'Ann',
},
{
id: '16hbajsabsd',
name: 'Leen',
},
{
id: 'nahs75a5sg',
name: 'Kris',
},
{
id: '667atsas',
name: 'Steve',
},
{
id: 'suudydjsjd',
name: 'Sarah',
},
],
};
}
onSelectedItemsChange = selectedItems => {
this.setState({selectedItems});
};
render() {
const {selectedItems} = this.state;
return (
<View>
<MultiSelect
hideTags
items={this.items}
uniqueKey="id"
ref={component => {
this.multiSelect = component;
}}
onSelectedItemsChange={this.onSelectedItemsChange}
selectedItems={selectedItems}
selectText="Pick friend(s)"
searchInputPlaceholderText="Search..."
onChangeInput={text => console.log(text)}
tagRemoveIconColor="#CCC"
tagBorderColor="#CCC"
tagTextColor="#CCC"
selectedItemTextColor="#CCC"
selectedItemIconColor="#CCC"
itemTextColor="#000"
displayKey="name"
searchInputStyle={{color: '#CCC'}}
submitButtonColor="#CCC"
submitButtonText="Submit"
/>
<View>
{this.multiSelect &&
this.multiSelect.getSelectedItemsExt(selectedItems)}
</View>
</View>
);
}
}
which is similar as in this tutorial, I did change some references to items with state - this to connect things to one another. Still the items are not loaded in items={this.items} I have the feeling.
Does anyone know why?
This is the error:
Thanks for your answer!
items prop is getting an undefined value because it's assigned this.items which doesn't exist in your class.
Change items={this.items} to items={this.sate.items} it makes more sense.
Another solution is to declare items array outside of the state.

Passing prop functions with "this" through a custom component to child component

I'm attempting to create a custom TextInput - I don't want to extend the TextInput class directly because I might be working with other libraries in the future. But I do want to pass the onChangeText function from the top-level of my script down through my custom component into the child TextInput
In the following script I've provided a default onChangeText function for my custom component that I would like to pass down to the TextInput. Obviously in this case, the "this" setState is not referring to the TextInput like I want it to. It's referring to my custom component.
And even if I could figure out how to get the default onChangeText to refer to the TextInput object's setState, how would I be able to refer to it when I actually use MyTextInput onChangeText=(text)=>???? at all ?
export class MyTextInput extends React.Component {
static propTypes = {
placeholder: PropTypes.string,
style: PropTypes.object,
onChangeText: PropTypes.func,
password: PropTypes.bool,
}
static defaultProps = {
password: false,
onChangeText: (text) => this.setState({text}),
}
render() {
return (
<TextInput
style={[styles.textinput, this.props.styles]}
placeholder={this.props.placeholder}
placeholderTextColor={colors.text}
secureTextEntry={this.props.password}
onChangeText={this.props.onChangeText}
/>
);
}
}
You see obviously the onChangeText in defaultProps has a "this" in it, referring to my component when that function to refer to the TextInput
What do you guys think is the best way to go about this?
Thanks!
Solution
You want to extend onChangeText to use parent components without change original acting.
Thus, link method for the onChangeText property as below.
export class MyTextInput extends React.Component {
state = {
value: '',
}
static propTypes = {
placeholder: PropTypes.string,
style: PropTypes.object,
onChangeText: PropTypes.func,
password: PropTypes.bool,
}
static defaultProps = {
password: false,
}
onChangeText = (value) => {
this.setState({ value });
this.props.onChangeText?.(); // Same as this.props.onChangeText && this.props.onChangeText()
}
render() {
return (
<TextInput
style={[styles.textinput, this.props.styles]}
placeholder={this.props.placeholder}
placeholderTextColor={colors.text}
secureTextEntry={this.props.password}
value={this.state.value}
onChangeText={this.onChangeText}
/>
);
}
}

how to pass form data from screen1 to screen2 in react native?

how to pass form data from screen1 to screen2 in react native ? I have following code in scrren1 I want posted amount data in screen2. Please let me know how can I pass data on screen2 and receive it in react native?
import React, { Component } from 'react';
import { Button, View, Text, StyleSheet } from 'react-native';
import t from 'tcomb-form-native'; // 0.6.9
const Form = t.form.Form;
const User = t.struct({
amount: t.String,
});
const formStyles = {
...Form.stylesheet,
formGroup: {
normal: {
marginBottom: 10
},
},
controlLabel: {
normal: {
color: 'blue',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
},
// the style applied when a validation error occours
error: {
color: 'red',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
}
}
}
const options = {
fields: {
amount: {
label: "Enter Amount You want to Top up",
error: 'Please add amount to proceed ahead!'
},
},
stylesheet: formStyles,
};
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
handleSubmit = () => {
const value = this._form.getValue();
console.log('value: ', value);
}
render() {
return (
<View style={styles.container}>
<Form
ref={c => this._form = c}
type={User}
options={options}
/>
<Button
title="Pay Now"
onPress={this.handleSubmit}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20,
backgroundColor: '#ffffff',
},
});
export default HomeScreen;
It depends if you want to pass data between Parent to Child, Child to Parent or Between Siblingsā€Š
I suggest you to read Passing Data Between React Components, old but this article did help me to understand the logic behind passing data as it's not as easy to implement as in other programming languages.
Excerpt using props:
class App extends React.Component {
render() {
[... somewhere in here I define a variable listName
which I think will be useful as data in my ToDoList component...]
return (
<div>
<InputBar/>
<ToDoList listNameFromParent={listName}/>
</div>
);
}
}
Now in the ToDoList component, use this.props.listNameFromParent to access that data.
You have many ways to send informations from one screen to another in React Native.
eg.
Use React Navigation to navigate between your scenes. You will be able to pass params to your components, which will be accessible in the navigation props when received.
this.props.navigation.navigate({routeName:'sceneOne', params:{name} });
You can also send directly props to a component, and treat them in it. In your render section of your first component, you could have something like this :
<myComponent oneProps={name}/>
In that example, you will receive the props "oneProps" in your second component and you will be able to access it that way :
type Props = {
oneProps: string,
}
class myComponent extends React.Component<Props> {
render() {
console.log('received sent props', oneProps);
return (
<View> // display it
<Text>{this.props.oneProps}</Text>
</View>
);
};
}
These are only two effective solutions, but there are a lot more.
Hope it helped you :)
Have a good day

React Native Pass properties on navigator pop

I'm using NavigatorIOS on my react native app. I want to pass some properties when navigating back to previous route.
An example case:
I'm in a form page. After submitting data, I want to go back to the previous route and do something based on the submitted data
How should I do that ?
Could you pass a callback func on the navigator props when you push the new route and call that with the form data before you pop to the previous route?
Code sample showing how to use a callback before pop. This is specifically for Navigator and not NavigatorIOS but similar code can be applied for that as well.
You have Page1 and Page2. You are pushing from Page1 to Page2 and then popping back to Page1. You need to pass a callback function from Page2 which triggers some code in Page1 and only after that you will pop back to Page1.
In Page1 -
_goToPage2: function() {
this.props.navigator.push({
component: Page2,
sceneConfig: Navigator.SceneConfigs.FloatFromBottom,
title: 'hey',
callback: this.callbackFunction,
})
},
callbackFunction: function(args) {
//do something
console.log(args)
},
In Page2 -
_backToPage1: function() {
this.props.route.callback(args);
this.props.navigator.pop();
},
The function "callbackFunction" will be called before "pop". For NavigatorIOS you should do the same callback in "passProps". You can also pass args to this callback. Hope it helps.
You can use AsyncStorage, save some value on child Component and then call navigator.pop():
AsyncStorage.setItem('postsReload','true');
this.props.navigator.pop();
In parent Component you can read it from AsyncStorage:
async componentWillReceiveProps(nextProps) {
const reload = await AsyncStorage.getItem('postsReload');
if (reload && reload=='true')
{
AsyncStorage.setItem('postsReload','false');
//do something
}
}
For NavigatorIOS you can also use replacePreviousAndPop().
Code:
'use strict';
var React = require('react-native');
var {
StyleSheet,
Text,
TouchableOpacity,
View,
AppRegistry,
NavigatorIOS
} = React;
var MainApp = React.createClass({
render: function() {
return (
<NavigatorIOS
style={styles.mainContainer}
initialRoute={{
component: FirstScreen,
title: 'First Screen',
passProps: { text: ' ...' },
}}
/>
);
},
});
var FirstScreen = React.createClass({
render: function() {
return (
<View style={styles.container}>
<Text style={styles.helloText}>
Hello {this.props.text}
</Text>
<TouchableOpacity
style={styles.changeButton} onPress={this.gotoSecondScreen}>
<Text>Click to change</Text>
</TouchableOpacity>
</View>
);
},
gotoSecondScreen: function() {
console.log("button pressed");
this.props.navigator.push({
title: "Second Screen",
component: SecondScreen
});
},
});
var SecondScreen = React.createClass({
render: function() {
return (
<View style={styles.container}>
<Text style={styles.helloText}>
Select a greeting
</Text>
<TouchableOpacity
style={styles.changeButton} onPress={() => this.sayHello("World!")}>
<Text>...World!</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.changeButton} onPress={() => this.sayHello("my Friend!")}>
<Text>...my Friend!</Text>
</TouchableOpacity>
</View>
);
},
sayHello: function(greeting) {
console.log("world button pressed");
this.props.navigator.replacePreviousAndPop({
title: "First Screen",
component: FirstScreen,
passProps: {text: greeting}
});
}
});
var styles = StyleSheet.create({
mainContainer: {
flex: 1,
backgroundColor: "#eee"
},
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
marginTop: 50,
},
helloText: {
fontSize: 16,
},
changeButton: {
padding: 5,
borderWidth: 1,
borderColor: "blue",
borderRadius: 4,
marginTop: 20
}
});
AppRegistry.registerComponent("TestApp", () => MainApp);
You can find the working example here: https://rnplay.org/apps/JPWaPQ
I hope that helps!
I had the same issue with React Native's navigator which I managed to solve using EventEmitters and Subscribables. This example here was really helpful: https://colinramsay.co.uk/2015/07/04/react-native-eventemitters.html
All I needed to do was update for ES6 and the latest version of React Native.
Top level of the app:
import React, { Component } from 'react';
import {AppRegistry} from 'react-native';
import {MyNavigator} from './components/MyNavigator';
import EventEmitter from 'EventEmitter';
import Subscribable from 'Subscribable';
class MyApp extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.eventEmitter = new EventEmitter();
}
render() {
return (<MyNavigator events={this.eventEmitter}/>);
}
}
AppRegistry.registerComponent('MyApp', () => MyApp);
In the _renderScene function of your navigator, make sure you include the "events" prop:
_renderScene(route, navigator) {
var Component = route.component;
return (
<Component {...route.props} navigator={navigator} route={route} events={this.props.events} />
);
}
And here is the code for the FooScreen Component which renders a listview.
(Note that react-mixin was used here in order to subscribe to the event. In most cases mixins should be eschewed in favor of higher order components but I couldn't find a way around it in this case):
import React, { Component } from 'react';
import {
StyleSheet,
View,
ListView,
Text
} from 'react-native';
import {ListItemForFoo} from './ListItemForFoo';
import reactMixin from 'react-mixin'
import Subscribable from 'Subscribable';
export class FooScreen extends Component {
constructor(props) {
super(props);
this._refreshData = this._refreshData.bind(this);
this._renderRow = this._renderRow.bind(this);
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([])
}
}
componentDidMount(){
//This is the code that listens for a "FooSaved" event.
this.addListenerOn(this.props.events, 'FooSaved', this._refreshData);
this._refreshData();
}
_refreshData(){
this.setState({
dataSource: this.state.dataSource.cloneWithRows(//YOUR DATASOURCE GOES HERE)
})
}
_renderRow(rowData){
return <ListItemForFoo
foo={rowData}
navigator={this.props.navigator} />;
}
render(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
)
}
}
reactMixin(FooScreen.prototype, Subscribable.Mixin);
Finally. We need to actually emit that event after saving a Foo:
In your NewFooForm.js Component you should have a method like this:
_onPressButton(){
//Some code that saves your Foo
this.props.events.emit('FooSaved'); //emit the event
this.props.navigator.pop(); //Pop back to your ListView component
}
This is an old question, but currently React Navigation's documentation for Passing params to a previous screen suggests that we use navigation.navigate() and pass whatever parameters we want the previous screen to have.