Javascript - ReactNative user input array - javascript

Im very new to react-native and javascript. I dont really know how to start this little project
users can select 1 option for each question
what pet do you prefer?
a dog
a cat
what color do you want your pet to be
red
green
Based on what they selected, an image (e.g.: a red dog) will appear.
I dont really know wether I should store the answers in an array and how to load the questions?

The most important concept of any react-native (and react for that matter) app is the concept of component.
What you want is to make a component "Question" that can receive via props the question's title and its possible answers. This way you can avoid repeating code when making different questions. I'll let you play with the inner implementation, should be easy enough searching for examples using checkboxes and labels.
Then, from your root component (or the component where you want to display these questions), you instance two of these Question with the respective data. This would look something like:
const App = () => {
return (
<View>
<Question
title="What pet do you prefer?"
answers=[
"A dog",
"A cat"
]
/>
<Question
title="What color do you want your pet to be?"
answers=[
"red",
"green"
]
/>
</View>
)
}
You can also add a button so that clicking it shows the corresponding image.
Finally, what you will need is to have the answers available in your root component, so that you can get the image via a request or however you were going to get it. After that, you just need to use it.
To do this, I defined state within the App component, I passed props to several components, and used some defined react native components. You should read about all of these in react native's docs, which are pretty good.
Here is the example:
const App = () => {
const [state, setState] = useState({})
const [image, setImage] = useState(null)
// This is useful for changing this component's state from within the children
const changeAnswer = ({question, answer}) => {
setState({...state, question: answer})
}
const getImage = async () => {
const { data } = await apiCall(state) // Or however you were going to get the image
setImage(data.image)
}
return (
<View>
<Question
changeAnswer={changeAnswer}
title="What pet do you prefer?"
answers=[
"A dog",
"A cat"
]
/>
<Question
changeAnswer={changeAnswer}
title="What color do you want your pet to be?"
answers=[
"red",
"green"
]
/>
<Button
title="Show image!"
onPress={getImage}
/>
{image &&
<Image src={image} />
}
</View>
)
}
Try to experiment and see where it gets you and what you can learn! The most important part when learning a new technology is to experiment.
Just out of curiosity, are you using expo to run your react native app?

I hope you will enjoy learning about Reactjs and the world of Javascript.
Firstly, I've noticed that you have tagged both Reactjs and React-native. Syntactically they are almost the same, but one is used to render to native platform UI to display as apps on smartphones, and the other is used for HTML DOM to display in internet browsers.
Nevertheless, I have made an example for you in Reactjs. Now React-native does not use the HTML dom (which is what is rendered in the example), but the logic will be somewhat the same!

Related

i18next <Trans> avoid retranslation of nested components

I am using i18next with React for my project. For most of the translations I use the t() function with normal text. However, for some cases I would prefer to use the <Trans /> component.
I prepared some example below:
function Example() {
const { t } = useTranslation();
const reusableComponent = <p>{t('TranslateMeOnlyOnce')}</p>;
return (
<>
{reusableComponent}
<Trans i18nKey={'OnlyOnceWrapper'}>
The reusable component says {reusableComponent}
</Trans>
</>
)
}
Which expect would my ranslation file to look something like this:
...
TranslateMeOnlyOnce: 'Translate me only once',
OnlyOnceWrapper: 'The reusable component says <1>Translate me only once</1>',
...
However, as you can see I would be required to translate the text of the nested component twice. I would really like to just translate the The reusable component says {reusableComponent} in the second translation string and not all of the first translation string for another time.
For those wondering why I would want to do this, lets say const reusableComponent is a Create Button with some function to create a new entry placed at the top of the Screen. When however, there is no entry displayed in my list of entries I want to display a message saying something like: 'Unfortunately there is no entry yet. Click on [CreateButtonIsDisplayedHere] to create a new entry'.
Let's say I experience that users find a translation 'New' instead of 'Create' more useful or the other way round, I would want to change only the translation of the button and not every other place containing this button.
I also found myself a solution to this issue already, however, in my experience this is super ugly to maintain as I need to pass the content as string and not as React child Element which pretty much take all advantages of using the <Trans /> Component and I could better use the t() function using sprintf.
function UglySolution() {
const { t } = useTranslation();
const reusableComponent = <p>{t('Translate me only Once')}</p>;
return (
<>
{reusableComponent}
<Trans
i18nKey={'OnlyOnceWrapper'}
components={[reusableComponent]}
variables={{ v: t('Translate me only Once') }}
defaults='The reusable component says <1>{{v}}</1>'
/>
</>
)
}
Which would expect my translation file to look something like this:
...
TranslateMeOnlyOnce: 'Translate me only once',
OnlyOnceWrapper: 'The reusable component says <1>{{v}}</1>',
...
So my question is: Can I make my translation file to look something like the second example I created without using the ugly <Trans /> component from the second example and more something like the component in the first example I created?
Resusing translations
There is a way using the <Trans /> component as in the first example with reusing previous translations. Using $t(KeyToAnotherTranslation) inside the translation string.
This does not fully solve the issue as it's also not too handy to maintain but would be a lot prettier than the second example shown.
function Example() {
const { t } = useTranslation();
const reusableComponent = <p>{t('TranslateMeOnlyOnce')}</p>;
return (
<>
{reusableComponent}
<Trans i18nKey={'OnlyOnceWrapper'}>
The reusable component says {reusableComponent}
</Trans>
</>
)
}
(Taken 1:1 from the code from the question)
With a translation file looking something like this:
...
TranslateMeOnlyOnce: 'Translate me only once',
OnlyOnceWrapper: 'The reusable component says <1>$t(TranslateMeOnlyOnce)</1>',
...
More information about variable usage in i18next.

Should I nest screens within the navigator function?

I'm currently developing an App in which multiple screens use the same data. Rather than declaring multiple loose functions and passing the same props everytime, I've nested them all in the same function that returns the navigator, which contains all data I need to use between the screens.
...imports...
const Drawer = createDrawerNavigator();
const HomeNav = ({route, navigation }) =\> {
... states, variables, and functions
const Scr1 = ({ navigation }) => {
... stuff with states and data above
);
}
const Scr2 = ({ navigation }) => {
... other stuff
);
}
return (
<Drawer.Navigator screenOptions={{drawerStyle:{backgroundColor:'#000000'}, drawerActiveTintColor:'#ffffff', drawerInactiveTintColor:'#DADADA'}}
initialRouteName="Casa"
drawerContent={(props) => <CustomDrawerContent {...props} />}>
... drawers
</Drawer.Navigator>
)
that's a cut down version of my code, to explain better. My screens, Scr1 and Scr2, are in the same function as the navigator.
Things work most of the time. I can easily share data between screens, but I'm having a LOT of problems with undefined objects and useless rerenders. I created a hook with the data that's getting loaded as the state, so whenever it efectively loads, the screen gets rerendered. But I have multiple data variables, so the hook gets called as each one finishes loading. And if I remove those hooks, I lose my defined objects.
Anyway, bottom line, my question is if what I'm doing could give me problems in the future, if it's the 'right' way to do things as to not complicate further down the road. If so, I find a way to deal with the rerenders, otherwise I change my code while I'm still beggining.
Full Homenav.js on github
That's the complete file, if it helps
pretty messy, but I'm learning react so I'm just going at it
Thanks in advance!

Creating a custom React Native component which automatically wraps inner text substrings with another component

I have the following component structure being rendered:
<Text>
Hello <UsernameWrapper>#gaberocksall</UsernameWrapper>! Nice to meet you.
</Text>
I would like create a custom component which automatically places #usernames into a UsernameWrapper, like this (and it should be rendered identically to the above snippet):
<AutoUsernameText>Hello #gaberocksall! Nice to meet you.</AutoUsernameText>
Or, even more generally, something along the lines of:
<MagicWrapper wrap={/#\w+/gi} wrapper={UsernameWrapper}>
Hello #gaberocksall! Nice to meet you.
</MagicWrapper>
How would I go about creating the MagicWrapper component in React Native?
We could just create a new component where we pass the username as a prop. In my opinion that is the way to go in react-native. Here is a quick implementation.
export const AutoUsernameText = (props) => {
return (
<Text>
Hello <UsernameWrapper>#{props.name}</UsernameWrapper>! Nice to meet you.
</Text>
)
}
Then, we can just use it as follows.
const SomeOtherScreen = () => {
return (
<AutoUsernameText name="gaberocksall" />
)
}
Notice that this solution depends on how UsernameWrapper is actually implemented. Since I do not know that, I assume plain strings here.
If you really want to pass it as a child, e.g.
<MagicWrapper>Hello #gaberocksal! Nice to meet you.</MagicWrapper>
then AutoUsernameText needs to parse its children and return the component you want instead. This is possible, although I do not see any good reason why one should do that. It opens many pitfalls that are not easy to avoid.
Nevertheless, we can achieve this as follows assuming that the child is a simple string using plain JS regular expressions.
export const MagicWrapper = (props) => {
// Notice that I do not guard here for potential errors ...
// props.children could be anything ...
const child = props.children
const name = child.match(/#\S+?(?=!)/g)
const content = child.split(name)
return (
<Text>
{content[0]}<UsernameWrapper>{name}</UsernameWrapper>{content[1]}
</Text>
)
}
In order to splitting specific Regex pattern like as username, url and etc, you are able to use react-native-parsed-text package. For example it's will help you changing color of the Regex pattern (also defining custom styles) or defining onPress function to doing some things. I hope it's be useful.
react-native-parsed-text github page.

How to use the key attribute do in an img tag?

I have this code to make an array of images to display on the screen. However, I don't know what the key attribute in the images array means. Ideally I want to change the src of the image whenever one of the images is clicked. If I add in an id or className property to the image, and look it up using document.getElementById i get this warning: Warning: Prop `%s` did not match. Server: %s Client: %s%s when I rendered the page. I am using react and razzle for this project. Can someone tell me how to accomplish this?
var shuffle = require("shuffle-array"),
animals = [
"frog0",
"frog1",
// ...
"seal1",
"armadillo0",
"armadillo1"
];
function whatami(img) {
alert(img);
}
var images = shuffle(animals).map(image => {
return (
<img
onClick={() => {
whatami(image);
}}
key={image}
src={"/animalgameback.jpg"}
/>
);
});
const App = () => (
<div>
<h3>My razzle app</h3>
<div>{images}</div>
</div>
);
export default App;
There quite a few things wrong with your approaches here. I would highly suggest reading through the official React documentation on how to write Javascript in ReactJS.
Let's cover some basics. Firstly, you really should not ever be using document.getElementById in React (unless the situation is dire and you are trying to hack apart a third party library). For the most part, you use the prop ref to reference React Nodes that are mounted in the DOM. But, just some advice for those learning, have your fun with using references so that you know how to use them and what they do. But. I would suggest that if you "need" a reference or "need" to directly talk to a React component at run-time, you might be doing something wrong.
Now, since you are attempting to "change" something based on user events or interactions, this is a perfect use-case for state management. React comes with the ability for each component to self-encapsulate a stateful object and use these states to "trigger" or re-render things in components, due to this update or change.
What is a key? It is a unique identifier that you can use on each rendered JSX component that shows the virtual DOM and the real DOM, that this component is intended to be re-rendered as is rather than unmounted, changed and re-mounted. A key allows React to keep track of which components were intended versus just respawned or mounted. You always write a key of a JSX element as a unique id. If you made 2 id's the same (try it out and see :) ) you would notice that they render as 1 on the screen and one replaces the other.
Heres how I would write this:
I have made a single image as a "viewer" to show which image was clicked, along with a click event handler attached to the image to change the state. The render function detects the image source change and re-renders the component. Therefore, the new source is received and used and rendered.
The Code
import React, { Component } from 'react';
const ANIMAL_DATA = [
'frog0','frog1','sheep0','sheep1','snail0','snail1','mouse0','mouse1','bat0','bat1','walrus0',
'walrus1','giraffe0','giraffe1','zebra0','zebra1','dog0','dog1','octopus0','octopus1','hippo0',
'hippo1','camel0','camel1','pig0','pig1','rhino0','rhino1','rooster0','rooster1','panda0','panda1',
'turtle0','turtle1','raccoon0','raccoon1','polarbear0','polarbear1','lion0','lion1','bison0',
'bison1','orca0','orca1','snake0','snake1','shark0','shark1','toucan0','toucan1','butterfly0',
'butterfly1','anteater0','anteater1','seal0','seal1','armadillo0','armadillo1'
]
class App extends Component {
constructor(props) {
super(props);
this.state = {
imageSource: 'animalgameback',
};
}
render() {
const { imageSource } = this.state;
return (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
<img style={{ width: 143, height: 'auto' }} source={require(`/${imageSource}.jpg`)} />
{ this.renderAnimalImages() }
</div>
);
}
renderAnimalImages() {
let images = [];
ANIMAL_DATA.forEach((animal, animalIndex) => {
// Be careful when assigning "onclick" methods to images,
// you are better off sticking with W3C rules on this. Use elements
// meant for "clicking" or "being clicked", i.e. <a>, <button>, etc
images.push(
<img
src={`/${animal}.jpg`}
key={`anima_image_${animalIndex}`}
onClick={this.__handleImageClick(animal)} />
);
});
return images;
}
__handleImageClick = animal => event => {
this.setState({ imageSource: animal });
};
}
export default App;
The key attribute serves as an identity declaration. It helps rendering engine to decide which elements should be re-rendered.
It's well explained in the documentation.

React context to change components

I'm building a dashboard that displays multiple graphs on the main page.
A graph has additional filter functions (change min max). The graph is in one component with the control elements and the display for the min/max is in another component.
I want to change the Min/Max display when the graph has changed (i.e. after switching the control element)
Currently my components look like this:
graph.js
var controllElements = (<button onClick={() => this.minmax('min)>..
<button onClick={() => this.minmax('max)>)
return(
<div>
{graph}
{controllElements}
</div>)
minmaxview.js
return(
<div>{value}
</div>)
I do not use redux in this project and it is not possible to use it in this project.
So I thought I would use the react context api but have no experience with it.
In the end, it should look like this.
If button min is clicked (graph.js) => Change value in minmaxview.js
My question is now. Can I use react context for this? If possible, are there any good examples? Or does anyone have another solution?
If necessary through sessions (Probably not possible due to an infinite loop in the compUpdate when setting state)
Thank you in advance.
You would do something like below to utilize React Context API's Provider/Consumer concept. Clearly it will need to be tailored within your app on however it is structured but based on minimal example that you provided, I tried my best to come closely to do Provider/Consumer psuedo react-code.
//graph.js
export const GraphContext = React.createContext(0); // asuming default for min/max is 0
var controllElements = (<button onClick={() => this.minmax('min)>..
<button onClick={() => this.minmax('max)>)
return(
<div>
{graph}
<GraphContext.Provider value={this.state.graphValuesObject}>
{/* Assume minmax function will update graphValuesObject that can be passed around */}
{controllElements}
</GraphContext.Provider>
</div>)
//minmaxview.js
import GraphContext from './graph.js';
return (
<GraphContext.Consumer>
{graphValuesObject => <div>{value}</div> }
</GraphContext.Consumer>
)
More info here: https://reactjs.org/docs/context.html

Categories