Changing the Text of a ReactNative <Text/> component programmatically without using state - reactjs

Hello StackOverflow Members,
I have a parent react-native component <Cell/> and inside it there is a child <Text/> component. The child component has to be a <Text/> component, i can not change it to another component. See example below...
let renderedCell = (<Cell>
<Text>HELLO PEOPLE</Text>
</Cell>)
I want to programmatically change "HELLO PEOPLE" to "Hello World" without using state via a callback. I have "inspected" the child component and i get this using console.log(renderedCell.props.children)...
{
$$typeof: Symbol(react.element)
type:
$$typeof: Symbol(react.forward_ref)
render: ƒ Text(props, forwardedRef)
displayName: "Text"
propTypes: {ellipsizeMode: ƒ, numberOfLines: ƒ, textBreakStrategy: ƒ, …}
__proto__: Object
key: null
ref: null
props:
style: (3) [(...), (...), (...)]
children: "HELLO PEOPLE"
__proto__: Object
_owner: FiberNode {tag: 1, key: null, stateNode: Table, elementType: ƒ, …}
_store: {validated: true}
_self: null
_source: {fileName: "component.js", lineNumber: 146, columnNumber: 17}
__proto__: Object
}
I have tried, to change the text programmatically from "HELLO PEOPLE" to "Hello World" like this:
renderedCell.props.children.props.children = "Hello World"
but the above does not work... I have tried cloning the element and other things... All I need is to change the text inside the component programmatically on the fly without using states from a callback.
Any suggestions? Is this permitted? Any other way to do it and accomplish the same?
TIA!

Instead of Text component use
<TextInput editable={false} defaultValue={"HELLO PEOPLE"} ref={(ref)=> this.textInputRef=ref} />
then you can directly manipulate the TextInput like this
if(this.textInputRef){
this.textInputRef.setNativeProps({text:"Change Text Here"})
}
Note: You can't directly manipulate a <Text> because it uses an NSTextStorage and not an NSString property. If there was a way to convert a string value to an NSTextStorage value, then we could also manipulate the Text field, but to my knowledge, we can't.

Related

Object is successfully passed as props but individual properties are 'undefined'

so I am passing an object from my App component to a child component.
Within the render method I am able to see the value of my object by using console.log as so
this.props.data.documents[0]
I am able to see the key value pairs of the object contained at documents[0],
but when I try to single out the individual properties (like below) I get an undefined error.
this.props.data.documents[0].username
This is what console.log(this.props.data.documents[0]) prints
{user_id: "5dfd9f8afc5bdf1bef11b78c", username: "christine", name: "second", src: "../images/5dfd9f8afc5bdf1bef11b78c_second.png"}
user_id: "5dfd9f8afc5bdf1bef11b78c"
username: "christine"
name: "second"
src: "../images/5dfd9f8afc5bdf1bef11b78c_second.png"
And here is the error for console.log(this.props.data.documents[0].src
react-dom.development.js:25206 Uncaught TypeError: Cannot read property 'src' of undefined
Why could this be? Thanks in advance.

Why do I get TypeError: _this.data.forEach is not a function

I am trying to retrieve data from the backend. These are the relevant parts of my code:
API call
getData(PrimaryId:number):Observable<DataDto[]>{
return this.httpClient.get(`${this.prefix}/<xyz>/${PrimaryId}/xyz`) as Observable<DataDto[]>
}
Component TypeScript
onRetrieveClicked() {
this.xyzService.getData(this.PrimaryId).subscribe(
(xyz: DataDto[]) => {
this.xyz = xyz
console.log(this.xyz)
console.log(this.xyz.forEach((data)=>data.name)
})
}
First console.log output
{content: Array(1), pageable: {…}, totalPages: 1, totalElements: 1, last: true, …}
content: Array(1)
0: {name: max, name: null, asset: null, …}
length: 1
..........
But when I try to print only the name in the second console, it says that forEach is not a function. How can I solve this
edit
Dto model
export interface DataDto {
name: string
asset: abcDto
status: StatusDto
tasks: efgDto[]
nextDate: string
}
The xyz variable that you type as DataDto[], an array, is actually an object. This can be seen in your console.log, an array would be enclosed in [], not {}
is an object --> {
content: Array(1), pageable: {…}, totalPages: 1, totalElements: 1, last: true, …}
content: Array(1)
0: {name: max, name: null, asset: null, …}
length: 1
}
The data you are looking for is most likely the response object's content so add an import for import {map} from 'rxjs/operators'; and transform the data you've gotten from the response:
this.xyzService.getData(this.PrimaryId).pipe(
map((xyzResponse: any) => xyzResponse.content)
).subscribe(
(xyz: DataDto[]) => {
this.xyz = xyz;
console.log(this.xyz);
let dataNames = xyz.map(data => data.name);
console.log(dataNames);
}
I've typed xyzResponse as any but you could ofcourse create a reusable type for it if the API always returns the object with content, pageable, totalPages, ...
Rxjs is the library that Angular uses to handle asynchronous programming, such as HTTP calls or component events. Rxjs chains asynchronous manipulations together in a pipe (hence the .pipe call). Inside of this pipe rxjs expects a chain of operators that will perform operations on the asynchronous data, one by one. The map operator takes the input value and returns a new value so that the value you subscribe to has been transformed from the HTTP response to the .content field of the HTTP response.
Working in this way fixes all TypeScript compiler errors and allows you to chain additional calls later, like retrying if the API times out, or catching errors, or merging in other HTTP calls.
It seems that your this.xyz is not an array, but has an array property called content, you should modify your response object in order to accept it.
You can check if your objects are arrays with the following method
Array.isArray(obj)
Update your code to this.
this.xyzService.getData(this.PrimaryId).subscribe(
(xyz: NewObject) => {
this.xyz = xyz
console.log(this.xyz)
//If you have doubts of what is comming is nice to check if your property is an array
if(Array.isArray(this.xhy.content) {
console.log(this.xyz.content.forEach((data)=>data.name) });
}
}
Create a new object in order to support your response
class NewObject {
content: Array<DataDto>
// other values here
}
Another approach is like #Robin says in the comment
this.xyzService.getData(this.PrimaryId).subscribe((xyz: {content: DataDto[]}) =>
{
this.xyz = xyz
console.log(this.xyz)
//If you have doubts of what is comming is nice to check if your property is an array
if(Array.isArray(this.xhy.content) {
console.log(this.xyz.content.forEach((data)=>data.name) });
}
}
It's because you are trying to loop through an object instead of array
I think you can try this:
console.log(this.xyz.content.forEach((data)=>data.name) })

jest shallow render find by class excluding

I am testing a class like:
const wrapper = shallow(<Features {...props}/>);
expect(wrapper.find('.Feature__Item:not(.Feature__Showmore)').length).toBe(2);
But, I am getting an error like this:
Error: Enzyme::Selector does not support pseudo-element or pseudo-class selectors.
How can I work around this limitation?
Update: Interesting observation, when I wrote the following in the debug console: wrapper.find('.Feature__Item').findWhere(a => a.hasClass('Feature__Showmore') === false).length
The output was:
0:Object {nodeType: "host", type: "li", props: Object, …}
1:Object {nodeType: "function", type: , props: Object, …}
2:Object {nodeType: "host", type: "li", props: Object, …}
3:Object {nodeType: "function", type: , props: Object, …}
I found another way which did the trick:
expect(wrapper.find('.Feature__Item').not('.Feature__Showmore').length).toBe(2);
You could try with findWhere:
expect(wrapper.find('.Feature__Item').findWhere(a => a.hasClass('Feature__Showmore') === false).length).toBe(2);
This will first get all the elements having class Feature__Item and then exclude from this list the ones which don't have Feature__Showmore class.

Is there a way I can use uri instead of require for React Native Prop types?

When I try to use uri, the image does not show and I want to avoid local images because I can't dynamically use require.
static propTypes = {
...ViewPropTypes,
initialPage: PropTypes.number,
pager: PropTypes.instanceOf(IndicatorViewPager),
tabs: PropTypes.arrayOf(PropTypes.shape({
text: PropTypes.string,
iconSource: Image.propTypes.source, <-- Here is the problem
selectedIconSource: Image.propTypes.source
})).isRequired,
}
Is there a way I can make this accept uri sources? Also here is the rest of my code:
let tabs = [{
text: `${this.state.tags.toLowerCase()}`,
iconSource: 'https://www.exmaple.com/img/cookies.png',
selectedIconSource: require("../img/bluep.png")
}];
I think you are mixing a couple of things. An Image source can be a require() or a uri, but the uri is not just a string you can pass to the source prop of the Image. It needs to be an object that has a uri string prop, like this:
<Image source={{uri: 'http://myimageurl.com'}}/>
Second, with prop types you are just defining which props validation you want to use at Debug time. It has nothing to do with your Image not showing up. If you send a wrong prop, it will show you a Yellow warning screen.
Lastly, your problem is that you are just sending the prop as a string, and you need to send the object.
So, without seeing much of the rest of the code, you should be fine changing this part:
let tabs = [{
text: `${this.state.tags.toLowerCase()}`,
iconSource: {uri: 'https://www.exmaple.com/img/cookies.png'},
selectedIconSource: require("../img/bluep.png")
}];

use react render a nested object in jsx

I am trying to render a nested object from a firestore database in react jsx with the map function. The 'text' property like 'I will save document" is contained within what appears to be an object(document) that contains an array[blocks] that contains another array[inlineStyleRanges] that contains an object (text). I am trying to render the text object and I am having a hard time figuring out the syntax to make it render correctly.
The object has this structure:
(4) [{…}, {…}, {…}, {…}]
0:
description: "Lorem ipsum dolor sit amet, suspendisse velit"
document:
blocks: Array(1)
0:
data: {}
depth: 0
entityRanges: []
inlineStyleRanges: []
key: "8u7m4"
text: "I will save this document."
type: "unstyled"
__proto__: Object
length: 1
__proto__: Array(0)
entityMap: {}
__proto__: Object
id: Timestamp {seconds: 1551856294, nanoseconds: 879000000}
title: "TCC/NIBF SUMMARY SHEET"
__proto__: Object
my attempted render method looks like this:
render() {
const urlID = this.state.urlID;
const results = this.state.documents;
const postList = results.map((result, index) => {
if (urlID === result.id.seconds.toString()) {
return (
<Card key={result.id.seconds}>
<CardBody>
<CardTitle>File Name: {result.title}</CardTitle>
<CardText>File Description: {result.description}</CardText>
{/*<CardText>File Document Text: {result.document... }</CardText>*/}
</CardBody>
</Card>
);
} else {
return null;
}
});
return <div>{postList}</div>;
}
I used the new es6 Map function to make a new object map that I turned into an array using the spread operator and then looked at the structure of the object as an array in chrome dev tools.
After studying it, I found the correct syntax:
<CardText>File text: {result.document.blocks["0"].text}</CardText>

Resources