React Native and Expo FS import issue - javascript

This line:
var a = require('react-native-fs');
returns the following error:
JSX value should be either an expression or a quoted JSX text(53:22)
Thank you for your help.
import React from 'react';
import {
StyleSheet,
View,
Text,
TextInput,
} from 'react-native';
export default class Component1 extends React.Component {
constructor(props) {
super(props);
this.state = {
textInputValue: "",
}
}
render() {
if (!this.props.visible) {
return false;
}
return (
<View
style={styles.component}
>
<View style={styles.layouts}>
<View style={styles.layout1}>
<View style={styles.itemcontainer1}>
<View style={styles.itemcontainer1Inner}>
<View style={styles.item1}>
<TextInput
style={styles.item1TextInput}
placeholder={"b"}
underlineColorAndroid={"transparent"}
placeholderTextColor={"rgba(0,0,0,1)"}
onChangeText={(val) => this.setState({ textInputValue: val })}
value={this.state.textInputValue}
var a = require"react-native-fs";
var path = a.DocumentDirectoryPath + '../textfile.txt';
a.writeFile(path, this.state.textInputValue, 'utf8');
.then((success) => {
console.log('File Written');

JSX is an extension to javascript commonly used by react applications. It looks similar to html tags, with angle brackets at the start and end of elements, and properties on those elements. If you want to put normal javascript inside of JSX, you can do so by using curly brackets. You've got examples of that, such as this one, which has some JSX for the View, then switches back to javascript to pass in styles.layouts:
<View style={styles.layouts}>
You're getting an error because you put javascript code in a random spot, without using curly brackets. Leaving out some of the extra stuff, you wrote:
<TextInput var a = require ("react-native-fs");
Which isn't valid JSX, since you didn't use curly brackets to switch back to javascript. Furthermore, this isn't the right spot to be doing asynchronous stuff, such as writing to a disk. The render method is a synchronous block of code which takes the current state of the component, and returns a description of what you want the screen to look like.
Where to put this code depends on what you're trying to do. If this is something you want to happen when the component is first created, then you'll want to put the code in componentDidMount, which is a function that react components can have to let them run code when they are first mounted. For example:
import fs from 'react-native-fs';
export default class Component1 extends React.Component {
componentDidMount() {
var path = fs.DocumentDirectoryPath + '../textfile.txt';
fs.writeFile(path, this.state.textInputValue, 'utf8')
.then((success) => {
console.log('File Written');
// Possibly call this.setState if you want Component1 to render with something different now that the write is complete
})
}
render() {
// similar render to before, but without the file system code
}
}
Or if this code is supposed to be run when they click a button, you might do it something like this:
onButtonPressed() {
var path = fs.DocumentDirectoryPath + '../textfile.txt';
fs.writeFile(path, this.state.textInputValue, 'utf8')
//etc
}
render() {
return (
// other components omitted for brevity
<Button onPress={() => this.onButtonPressed()}/>
)
}

Related

Can't get MDX to work in a JavaScript Component. Just want Markdown to be turned to react components

Trying to make a react component with MDX that will turn markdown into react components. I want this so I can transform a blog article with embedded components (such as special article toolings and diagrams) from markdown stored in the DB into something presented on the page.
Been working on it for a few hours now and even my best attempts just result in literally nothing being displayed on the page. Where replacing the 'content' with a string displays.
I'm 100% baffled, lost and confused and really would appreciate help.
Original problem: https://www.reddit.com/r/reactjs/comments/mbdz7k/reactmarkdown_custom_component_declaration_how/
Working solution (but I am struggling to get it to work on my end). Provided by cmdq
https://codesandbox.io/s/dazzling-wescoff-ib8mv?file=/src/index.js
I've basically just moved the entire particular component file into being a JavaScript file but unfortunately I'm still running into many issues-- probably from lack of familiarality.
My current code is this in the file:
import ReactDOM from 'react-dom'
import renderToString from 'next-mdx-remote/render-to-string'
import hydrate from 'next-mdx-remote/hydrate'
import React, { Component } from 'react'
const exampleMdx = `
# h1
**foo**
<MyCustomComponent text="hell yeah" />
a
bar
`
const components = {
h1: (props) => <h1 style={{ color: 'tomato' }} {...props} />,
MyCustomComponent: ({ text }) => <marquee>{text}</marquee>,
}
const functionThing = async () => {
const mdxString = await renderToString(exampleMdx, {
components,
})
return hydrate(mdxString, { components });
}
export const ArticleTextTrial = () => {
let content = functionThing;
console.log(functionThing());
return (
<div className="articleMainTextSectionBody">{exampleMdx}
</div>
)
}

How to render React components, stored in an array, inside the render method of another component

I've got a component, let's call Screen, which imports a component that will contain a search result. A method in Screen loops through an array of objects and for each object instaniates a new search result component and stores them in an array.
I'm new to React, so my question is, is it correct to create an instance of a class extending Component using the new keyword, or is there a proper React way to do this? If so what is the correct way of rendering these components in the Screen class, as the way I'm doing it in the snippet below doesn't seem to work as nothing renders.
Screen.js - I've cut down the file to just show what I'm on about
import React, { Component } from "react";
import { Text, View } from "react-native";
import SearchResult from "components/SearchResult";
class Screen extends Component {
searchResultsComponents = [];
submitSearchQuery(searchParam /* Array of objects */) {
this.searchResultsComponents = [];
for(let result in this.searchResults) {
this.searchResultsComponents.push(new SearchResult(this.searchResults[result]));
}
this.forceUpdate();
});
}
render() {
return(
<View>
{ this.searchResultsComponents.forEach((it) => { it.render(); }) }
</View>
);
}
}
export default Screen;
SearchResult.js
import React, { Component } from "react";
import { Text, View } from "react-native";
class SearchResult extends Component {
title;
setTitle(title) {
this.title = title;
}
getTitle() {
return this.title;
}
description;
setDescription(description) {
this.description = description;
}
getDescription() {
return this.description;
}
constructor(searchResult) {
super();
this.setTitle(searchResult.snippet.title);
this.setDescription(searchResult.snippet.description)
}
render() {
return(
<View>
<Text>{ this.title }</Text>
<Text>{ this.description }</Text>
</View>
);
}
}
export default SearchResult;
The proper way to approach this is, inside your render method:
{ this.searchResultsComponents.map((it, index) => <it key={index} />) }
The key is optional, but recommended by react to improve rendering performance when rendering an array of components.
See https://reactjs.org/docs/lists-and-keys.html#basic-list-component for more details.
Your approach does not work, because by just calling the render method, the HTML DOM is returned by the function call. However, you just call the function and don't do anything with the return value.
The reason your approach doesn't work is that forEach method doesn't return anything. Instead of forEach use map. Your modified line should look something like this:
this.searchResultsComponents.map((component, index) => <View key={index}>{component}</View>);
The reason I wrapped the component into View is because this way each item can get key attribute which is recommended when displaying arrays.

How to consume the result of Image.prefetch in React Native?

I'm trying to prefetch an image using Image.prefetch() in an entry level component so that when the user navigates to a child component which displays the aforementioned image, it'll be ready to be displayed with less delay.
Image.prefetch() is mentioned under the Image topic in React Native documentation but I found the examples there fairly convoluted.
Can someone who used this feature explain how it would work for the simple use case I mentioned above?
Here's how I tried to implement it:
// main.js
class Main extends Component {
render() {
let prefetch = Image.prefetch(this.props.url);
return (
<Content imgUrl={this.props.url}/>
);
}
}
// content.js
class Content extends Component {
render() {
const { imgUrl } = this.props;
return (
<Image source={imgUrl} />
);
}
}
The result of this implementation is an empty Image because it just returns a promise without an Image.
What am I missing here?
You need to render Content after the prefetch is resolved:
render() {
Image.prefetch(this.props.url)
.then(() => {
return (
<Content imgUrl={this.props.url} />
);
})
.catch(error => console.log(error))
}
You may also show the ActivityIndicator while prefetching inside the Main render, conditional on a state variable that changes its flag (true/false) when the prefetch is resolved so that the indicator disappears on that event.

Render child components with Enzymejs tests

I'm trying to test a simple component that take some props (it have no state, or redux connection) with Enzyme, it works for the plain elements like <div /> and so on, but when i try to test if the element rendered by the child component exists, it fails.
I'm trying to use mount but it spit me a lot of errors, i'm new in this so, here is my code:
import React, { Component } from 'react';
import WordCloud from 'react-d3-cloud';
class PredictWordCloud extends Component {
render() {
const fontSizeMapper = word => Math.log2(word.value) * 3.3;
const { size, data, show } = this.props;
if (!show)
return <h3 className='text-muted text-center'>No data</h3>
return (
<section id='predict-word-cloud'>
<div className='text-center'>
<WordCloud
data={data}
fontSizeMapper={fontSizeMapper}
width={size}
height={300} />
</div>
</section>
)
}
}
export default PredictWordCloud;
It's just a wrapper for <WordCloud />, and it just recieves 3 props directly from his parent: <PredictWordCloud data={wordcloud} size={cloudSize} show={wordcloud ? true : false} />, anything else.
The tests is very very simple for now:
import React from 'react';
import { shallow } from 'enzyme';
import PredictWordCloud from '../../components/PredictWordCloud.component';
import cloudData from '../../helpers/cloudData.json';
describe('<PredictWordCloud />', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<PredictWordCloud data={cloudData} size={600} show={true} />)
});
it('Render without problems', () => {
const selector = wrapper.find('#predict-word-cloud');
expect(selector.exists()).toBeTruthy();
});
});
For now it pass but if we change the selector to: const selector = wrapper.find('#predict-word-cloud svg'); where the svg tag is the return of <Wordcloud /> component, the tests fails because the assertion returns false.
I tried to use mount instead of shallow, exactly the same test, but i get a big error fomr react-d3-cloud:
PredictWordCloud Render without problems TypeError: Cannot read property 'getImageData' of null.
This is specially weird because it just happens in the test environment, the UI and all behaviors works perfectly in the browser.
You can find your component directly by Component name.
Then you can use find inside your sub-component as well.
e.g
it('Render without problems', () => {
const selector = wrapper.find('WordCloud').first();
expect(selector.find('svg')).to.have.length(1);
});
or
You can compare generated html structure as well via
it('Render without problems', () => {
const selector = wrapper.find('WordCloud').first();
expect(selector.html()).to.equal('<svg> Just an example </svg>');
});

Why can't I curry a react component?

I've been getting started with react-redux and finding it a very interesting way to simplify the front end code for an application using many objects that it acquires from a back end service where the objects need to be updated on the front end in approximately real time.
Using a container class largely automates the watching (which updates the objects in the store when they change). Here's an example:
const MethodListContainer = React.createClass({
render(){
return <MethodList {...this.props} />},
componentDidMount(){
this.fetchAndWatch('/list/method')},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(oId){
this.props.fetchObject(oId).then((obj) => {
this._unwatch = this.props.watchObject(oId);
return obj})}});
In trying to supply the rest of the application with as simple and clear separation as possible, I tried to supply an alternative 'connect' which would automatically supply an appropriate container thus:
const connect = (mapStateToProps, watchObjectId) => (component) => {
const ContainerComponent = React.createClass({
render(){
return <component {...this.props} />
},
componentDidMount(){
this.fetchAndWatch()},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(){
this.props.fetchObject(watchObjectId).then((obj) => {
this._unwatch = this.props.watchObject(watchObjectId);
return obj})}
});
return reduxConnect(mapStateToProps, actions)(ContainerComponent)
};
This is then used thus:
module.exports = connect(mapStateToProps, '/list/method')(MethodList)
However, component does not get rendered. The container is rendered except that the component does not get instantiated or rendered. The component renders (and updates) as expected if I don't pass it as a parameter and reference it directly instead.
No errors or warnings are generated.
What am I doing wrong?
This is my workaround rather than an explanation for the error:
In connect_obj.js:
"use strict";
import React from 'react';
import {connect} from 'react-redux';
import {actions} from 'redux/main';
import {gets} from 'redux/main';
import {isFunction, omit} from 'lodash';
/*
A connected wrapper that expects an oId property for an object it can get in the store.
It fetches the object and places it on the 'obj' property for its children (this prop will start as null
because the fetch is async). It also ensures that the object is watched while the children are mounted.
*/
const mapStateToProps = (state, ownProps) => ({obj: gets.getObject(state, ownProps.oId)});
function connectObj(Wrapped){
const HOC = React.createClass({
render(){
return <Wrapped {...this.props} />
},
componentDidMount(){
this.fetchAndWatch()},
componentWillUnmount(){
if (isFunction(this._unwatch)) this._unwatch()},
fetchAndWatch(){
const {fetchObject, watchObject, oId} = this.props;
fetchObject(oId).then((obj) => {
this._unwatch = watchObject(oId);
return obj})}});
return connect(mapStateToProps, actions)(HOC)}
export default connectObj;
Then I can use it anywhere thus:
"use strict";
import React from 'react';
import connectObj from 'redux/connect_obj';
const Method = connectObj(React.createClass({
render(){
const {obj, oId} = this.props;
return (obj) ? <p>{obj.id}: {obj.name}/{obj.function}</p> : <p>Fetching {oId}</p>}}));
So connectObj achieves my goal of creating a project wide replacement for setting up the connect explicitly along with a container component to watch/unwatch the objects. This saves quite a lot of boiler plate and gives us a single place to maintain the setup and connection of the store to the components whose job is just to present the objects that may change over time (through updates from the service).
I still don't understand why my first attempt does not work and this workaround does not support injecting other state props (as all the actions are available there is no need to worry about the dispatches).
Try using a different variable name for the component parameter.
const connect = (mapStateToProps, watchObjectId) => (MyComponent) => {
const ContainerComponent = React.createClass({
render() {
return <MyComponent {...this.props} obj={this.state.obj} />
}
...
fetchAndWatch() {
fetchObject(watchObjectId).then(obj => {
this._unwatch = watchObject(watchObjectId);
this.setState({obj});
})
}
});
...
}
I think the problem might be because the component is in lower case (<component {...this.props} />). JSX treats lowercase elements as DOM element and capitalized as React element.
Edit:
If you need to access the obj data, you'll have to pass it as props to the component. Updated the code snippet

Categories