I know that in html and javascript are able to change it own css style by id and class , in react native, how to set / change the component style. I have map a list of component, and each of them have set a key value. When I call a function, I would like to change one of the component style.
eg: change the key is 2 component style
_RenderSpecialItem(){
return this.state.speciallist.map((item, i)=>{
return(
<SpecialItem
key={i}
/>
);
});
}
_ChangeStyle(){
//do something
}
You can use Direct Manipulation but it's not a good practice, for more please read
Direct manipulation will not be a tool that you reach for frequently; you will typically only be using it for creating continuous animations to avoid the overhead of rendering the component ...
in the link. Otherwise, you should you set state in component and change state to update the style
e.g.
first set ref to the component :
<SpecialItem
key={i}
ref={(thisItem) => this[`item-${i}`] = thisItem}
/>
then setNativeProps :
_ChangeStyle() {
this['item-2'].setNativeProps({style: {/* your style here */}});
}
full example
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
speciallist: ['aaa', 'bbb', 'ccc']
}
}
componentDidMount() {
this['text-0'].setNativeProps({style: {fontSize: "10"}});
this['text-1'].setNativeProps({style: {fontSize: "20"}});
this['text-2'].setNativeProps({style: {fontSize: "30"}});
}
render() {
return (
<View style={styles.container}>
{
this.state.speciallist.map((item, i)=>(
<Text
key={`text-${i}`}
ref={(thisItem) => this[`text-${i}`] = thisItem}
>
{item}
</Text>
))
}
</View>
);
}
}
Related
I'm building a React-Native app and trying to optimize it, i runned into the case of my Flatlist.
So this Flatlist basically renders few elements and each of these elements are selectable.
The issue i'm facing is that selecting one single item rerenders the whole Flatlist, and thus all items it contains.
I've seen a lot of solutions online already, and tried them without any success.
Here is my code :
Class component containing the Flatlist
const keyExtractor = (item) => item.id
export default class OrderedList extends Component {
state = {
selected: null,
}
onPressSelect = (id) => {
console.log(this.state.selected)
if(this.state.selected === id) {
this.setState({ selected: null})
}
else {
this.setState({ selected: id})
}
}
renderItemOrdered = ({item}) => {
const { group, wording, description, id: uniqueID } = item
const { id, name } = group
return (
<CategoryCard
type="ordered"
// item={item}
uniqueID={uniqueID}
groupName={name}
groupID={id}
description={description}
title={wording}
selected={this.state.selected}
onPressSelect={() => this.onPressSelect(item.id)}
/>
)
}
render() {
return (
<FlatList
initialNumToRender={10}
maxToRenderPerBatch={10}
data={this.props.data}
renderItem={this.renderItemOrdered}
keyExtractor={keyExtractor}
extraData={this.state.selected} ---> Tried with and without it
/>
)
}
}
Class component containing the renderItem method
export default class CategoryCard extends Component {
shouldComponentUpdate = (nextProps, nextState) => {
return nextProps.selected !== this.props.selected &&
nextProps.onPressSelect !== this.props.onPressSelect
}
render(){
if(this.props.type === 'ordered') {
return (
<Pressable style={this.props.selected === this.props.uniqueID ? styles.cardContainerSelected : styles.cardContainer} onPressIn={this.props.onPressSelect}>
<View style={[styles.cardHeader, backgroundTitleColor(this.props.groupID)]}>
<Text style={[styles.cardGroupName, textTitleColor(this.props.groupID)]}>{this.props.groupName}</Text>
</View>
<View style={styles.cardContent}>
<Text style={styles.cardTitle}>{this.props.wording}</Text>
<Text style={styles.cardDescription} numberOfLines={3} ellipsizeMode="tail">{this.props.description}</Text>
</View>
</Pressable>
)
}
}
}
What i already tried :
At first my components were functional components so i changed them into class components in order to make things works. Before that, i tried to use React.memo, also to manually add a function areEqual to it, to tell it when it should rerender, depending on props.
It didn't give me what i wanted.
I also tried to put all anonymous functions outside return statements, made use of useCallback, played around the ShouldComponentUpdate (like adding and removing all the props, the onPress prop, selected props)... None of that worked.
I must be missing something somewhere.. If you can help me with it, it would be a big help !
I have my class and I have a method and I am wondering if I could use props inside a mehtod.
Notice I try to use props in methodTwo. Is this possible? If not, is there a way I could use props in method?
import React from 'react';
import { Image, Text, View } from 'react-native';
export default class Test extends React.PureComponent {
methodOne = () => {
this.setState({
one:false,
two:false,
three:false
})
}
methodTwo = () => {
this.setState({
one:false,
two:false,
//I want to use props
three:this.props.three
})
}
render() {
return (
<View style={{ backgroundColor: 'transparent', alignItems: 'center' }}>
<Button title='one' onPress={()=>this.methodOne()}/>
// I could call i like this?
<Test three='newState'/>
</View>
);
}
}
methodTwo = () => {
this.setState({
one:false,
two:false,
three:this.props.three
})
}
props-> is the value that is been transferred from parent component to child component.
In class based component you fetch the value by using this.props.Attribute_name and in functional based component you can fetch the value using props.Attribute_name (mind functional based component dont have any concept of this)
if you want to use this.props.three ,then in parent component call (the component calling this particular component) <Test three="anyValue" /> then you can easily get this value in child component.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
We could just swap out the <p> for a <Cat> here ... but then
we would need to create a separate <MouseWithSomethingElse>
component every time we need to use it, so <MouseWithCat>
isn't really reusable yet.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}
The props are accessible to whole of the class scope with the syntax this.props.xxxx if you have passed it from its parent component. SO you can use in methodOne too.
You can use props inside a method. Any specific error you are facing ?.
I have created the following component:
type ToggleButtonProps = { title: string, selected: boolean }
export default class ToggleButton extends Component<ToggleButtonProps>{
render(){
return (
<TouchableWithoutFeedback {...this.props}>
<View style={[style.button, this.props.selected ? style.buttonSelected : style.buttonDeselected]}>
<Text style={[style.buttonText, this.props.selected ? style.buttonTextSelected : style.buttonTextDeselected]}>{this.props.title}</Text>
</View>
</TouchableWithoutFeedback>
);
}
}
The styles are simple color definitions that would visually indicate whether a button is selected or not. From the parent component I call (item is my object):
item.props.selected = true;
I've put a breakpoint and I verify that it gets hit, item.props is indeed my item's props with a selected property, and it really changes from false to true.
However, nothing changes visually, neither do I get render() or componentDidUpdate called on the child.
What should I do to make the child render when its props change? (I am on React Native 0.59.3)
You can't update the child component by literally assigning to props like this:
item.props.selected = true;
However, there are many ways to re-render the child components. But I think the solution below would be the easiest one.
You want to have a container or smart component which will keep the states or data of each toggle buttons in one place. Because mostly likely, this component will potentially need to call an api to send or process that data.
If the number of toggle buttons is fixed you can simply have the state like so:
state = {
buttonOne: {
id: `buttonOneId`,
selected: false,
title: 'title1'
},
buttonTwo: {
id: `buttonTwoId`,
selected: false,
title: 'title2'
},
}
Then create a method in the parent which will be called by each child components action onPress:
onButtonPress = (buttonId) => {
this.setState({
[buttonId]: !this.state[buttonId].selected // toggles the value
}); // calls re-render of each child
}
pass the corresponding values to each child as their props in the render method:
render() {
return (
<View>
<ToggleButton onPressFromParent={this.onButtonPress} dataFromParent={this.state.buttonOne} />
<ToggleButton onPressFromParent={this.onButtonPress} dataFromParent={this.state.buttonTwo} />
...
finally each child can use the props:
...
<TouchableWithoutFeedback onPress={() => this.props.onPressFromParent(this.props.dataFromParent.id)}>
<View style={[style.button, this.props.dataFromParent.selected ? style.buttonSelected : style.buttonDeselected]}>
...
I left the title field intentionally for you to try and implement.
P.S: You should be able to follow the code as these are just JS or JSX.
I hope this helps :)
Because children do not rerender if the props of the parent change, but if its STATE changes :)
Update child to have attribute 'key' equal to "selected" (example based on reactjs tho')
Child {
render() {
return <div key={this.props.selected}></div>
}
}
I am very a beginner at Javascript, ES6 and ReactNative. I have a class component called Input. there I have a method (handleTextLayout) in which I get the width of my Text. I need to use that number inside the render method, let's say i want to show how big the text it. How can I have a class variable and the then change it in that method and pass it in other places in the class? Below is what I tried (besides many other things) but it doesn't work.
class Input extends Component {
constructor(){
super();
Input.TextWidth = '11111';
}
handleTextLayout(evt){
Input.TextWidth = evt.nativeEvent.layout.width;
}
render(){
const { label, placeholder, onChangeText, value } = this.props;
const { inputStyle, labelStyle, viewContainerStyle } = myInputStyle ;
return(
<View style={ viewContainerStyle } >
<Text style={ labelStyle } onLayout={this.handleTextLayout.bind(this)}>
{ Input.TextWidth }
</Text>
<TextInput
style={ inputStyle }
placeholder={ placeholder }
onChangeText={ onChangeText }
value={ value }
autoCorrect={false}
/>
</View>
);
};
};
it still shows that 11111 in the Text.
EDIT
Some folks in comments pointed out mistakes in this code - I fixed those now, thanks!
I don't know anything about React Native but I assume this works the same as in React.
I guess what you could do is to create a state and save that width there.
Then in the handleTextLayout method you set state to new value.
so:
constructor(props){
super(props);
this.state = {
textWidth: '11111'
};
this.handleTextLayout.bind(this); // a good practice
}
and then
handleTextLayout(evt){
this.setState({ textWidth: evt.nativeEvent.layout.width });
}
and finally in render:
<Text style={ labelStyle } onLayout={this.handleTextLayout}>
{ this.state.textWidth }
</Text>
This will work because setState will re-render the component so it shows new value.
I’m using react native drawer of https://github.com/root-two/react-native-drawer
I am trying to call the method openDrawer() by passing in variable into NavigationBarRouteMapper. I tried logging inside NavigationBarRouteMapper, and it logs the variable passed in correctly. But when it used inside the NavigationBarRouteMapper, by clicking the Left Navigation button of ‘Open Drawer’, it does not do anything:
class drawerPractice extends Component {
...
openDrawer(){
this._drawer.open()
}
render() {
return (
<Drawer
content={<DrawerPanel/>}
openDrawerOffset={100}
ref={(ref) => this._drawer = ref}
type='static'
tweenHandler={Drawer.tweenPresets.parallax}
>
<Navigator
configureScene={this.configureScene}
initialRoute={{name: 'Start', component: Start}}
renderScene={this.renderScene}
style={styles.container}
navigationBar={
<Navigator.NavigationBar
style={styles.navBar}
routeMapper={NavigationBarRouteMapper(this.openDrawer)}
/>
}
/>
</Drawer>
);
}
}
var NavigationBarRouteMapper = openDrawer => ({
LeftButton(route, navigator, index, navState){
return(
<TouchableHighlight onPress={()=>{openDrawer}}>
<Text>Open Menu</Text>
</TouchableHighlight>
)
}
},...
Why may be the issue?
On your constructor() method add this: this.openDrawer = this.openDrawer.bind(this);
You're likely using this on the wrong scope.
There is a common confusion when working with React components and the new ES6 extends syntax. If you use React.createClass, it will bind this to all of your functions, but when using the ES6 approach of extends React.Component you have to bind your functions manually.
You can do it either inline using
<TouchableHighlight onPress={()=>{this.openDrawer.bind(this)}}>
Alternatively, you can add to your constructor, after super():
this.openDrawer = this.openDrawer.bind(this);
Personally I like this approach as I find this code a bit easier to read.
For more information about the ES6 way, check this link.