React - is the following true about how html tags' attribute values change when a component is reexecuted? - html

Can I verify my observation (based on the code below)?:
Class values of an element added in the html markup of a component don't neccessarily 'persist' - when the component is re-executed (perhaps as the result of a change in state), the class value from the last execution can disappear.
However, class values written via vanilla JS persist.
I'm always looking for best practices in code to avoid problems down the line. Is this code a reasonable way to add a class to a tag in React?
sandbox: https://codesandbox.io/s/purple-moon-epmmu?file=/src/styles.css:59-126
import "./styles.css";
import React, { useState, useEffect } from "react";
export default function App() {
let [color, setColor] = useState(false);
function updateColor() {
if (color) {
setColor("");
} else {
setColor("red");
}
}
useEffect(() => {
document.querySelector(`div`).classList.add(`green-text`);
}, []);
return (
<>
<div className={color ? "red" : null} onClick={updateColor}>
Click to add/remove red class
</div>
</>
);
}
.red {
background-color: red;
}
.green-text {
color: green;
}

I would highly recommend you to try out the classnames library to inject class attribute value dynamically. It's a very elegant library which helps avoid complicated code pertaining to css updates, when the codebase gets bigger.
I have tweaked your example, including some naming changes, removing unwanted code and added classnames. Also you can pass in any other class names from other components as such, and render it dynamically too. This is how you'd do it:
import "./styles.css";
import React, { useState, useEffect } from "react";
import classnames from "classnames"
export default function App(props) {
const [hasRed, setHasRed] = useState(false);
return (
<div className={classnames(
["green-text", props.otherClassNamesIfAny],
{
"red": hasRed,
}
)}
onClick={setHasRed(!hasRed)}
>
Click to add/remove red class
</div>
);
};

Related

how to isolate a file's css from another file's css in react

We are currently working on a project where,
project structure demo:
whenever i use pdf.js and pdf.css in seperate project then it works perfectly.But when i put that pdf js and css inside this project ,then the css of the projects overriding the pdf.css
is there any way to use separate css files each component?
i have tried doing modules.css ,but i have to change the existing all css for that,
please provide some suggestion
Here is a reference to React's docs. Basically you need to name your file {file_name}.module.css, where the file extension needs to end with module.css
Then you can use like this, as shown on React example:
Class based component
import React, { Component } from 'react';
import styles from './Button.module.css'; // Import css modules stylesheet as styles
import './another-stylesheet.css'; // Import regular stylesheet
class Button extends Component {
render() {
// reference as a js object
return <button className={styles.error}>Error Button</button>;
}
}
Functional base component
import React, { Component } from 'react';
import styles from './Button.module.css'; // Import css modules stylesheet as styles
import './another-stylesheet.css'; // Import regular stylesheet
const Button = () => {
return <button className={styles.error}>Error Button</button>
}

Getting html element attribute is unreliable

So i am trying to get a data attribute of a div element but it is giving a null but not always; after like ten times of showing a null, it shows the attribute so what am i missing? is it a bug?
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [toggleComments, setToggleComments] = useState(false);
const changetoggleState = (e)=> { setToggleComments(toggleComments ? false : true);
console.log(toggleComments); console.log(e.target.getAttribute('data-comment-data'))};
return <div data-comment-data="hello there" onClick={changetoggleState}>Hello World</div>
};
export default App;
Change your code to:
e.target.getAttribute("data-comment-data")
Online demo:
https://codesandbox.io/s/spring-paper-dsd1tt?file=/src/App.js:305-347
So the problem was i put the data attribute on the div but when i was clicking for some reason i was clicking on a PNG icon inside the div that did not have any data attribute. that is why it sometimes worked when i did not click on the PNG.

How to render component by adding the component using innerHTML?

I have a component A which only contain a div with an id and a buttons that renders a component inside the div using innterHTML document.getElementById('my-router-outlet').innerHTML = '<app-component-b-page></app-component-b-page>';. But this is not rendering I wonder why?.
I'm trying to avoid using ngIf to be a selector for which component should be rendered for performance reason. Also if I clear the innerHTML does the resources of that component will be cleared?
Okay so a few things here
innerHTML = '<app-component-b-page></app-component-b-page>' is never going to work, angular wont recognise the angular component tag from a innerHTML call
using *ngIf wont affect the performance of the page, so doing the following
<app-component-b-page *ngIf="value === true"></app-component-b-page>
is probably you best option here
If you really don't want to use *ngIf you can use #ViewChild and ComponentFactoryResolver
In your HTML
<!-- this is where your component will be rendered -->
<div #entry></div>
In your component
import { Component, OnInit, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '#angular/core'
import { YourComponent } from ... // import the component you want to inject
// ...
export class ...
#ViewChild('entry', {read: ViewContainerRef, static: true }) entry: ViewContainerRef;
constructor(
private _resolver: ComponentFactoryResolver
) {}
showComponent() {
const factory = this._resolver.resolveComponentFactory(YourComponent);
// this will insert your component onto the page
const component = this.entry.createComponent(factory);
}
// and if you want to dynamically remove the created component you can do this
removeComponent() {
this.entry.clear();
}
You are adding the element to the dom directly and it's not rendered by Angular.
You should go for the *ngIf.

How to navigate in React-Native?

I am using ReactNavigation library in my react-native project and since 6 hours I am trying to navigate from one screen to others screen and have tried every possible way but I think I am not able to get the logic properly.
This is my project structure.
Here
The way I am doing it.
const AppStack = StackNavigator({ Main: Feeds });
const AuthStack = StackNavigator({ Launch: LaunchScreen, });
export default SwitchNavigator({
Auth: AuthStack,
App: AppStack
});
In my LaunchScreen.js
const SimpleTabs = TabNavigator(
{
Login: {
screen: Login,
path: ""
},
SignUp: {
screen: SignUp,
path: "doctor"
}
},
);
<SimpleTabs screenProps={{rootNavigation : this.props.navigation }}/>
But the problem is in my LaunchScreen Component there is a TabNavigator which contains my other two components Login.js and SignUp.js but the button in my Login.js doesn't navigate it to Feed.js.
When you click on the button this is performed.
signInAsync = async () => {
await AsyncStorage.setItem('userToken', 'abc');
this.props.navigation.navigate('Main');
console.log("AAAAAsSSS");
};
My LaunchScreen.js contains a TabNavigation which lets you slide between two components ie. Login.js and SignUp.js.
Now when you click on the Login button which is in Login.js component it will authenticate the user and will switch the entire LauchScreen.js component with the Feed.js component.
I am a noob to react-native.
You can use react-native-router-flux (npm install --save react-native-router-flux)
just make one Navigator.js file and define each page you wanted to navigate.
import React from 'react';
import { Router, Scene } from 'react-native-router-flux';
import LaunchScreen from '../components/LaunchScreen.js';
import Feed from '../components/Feed.js';
const Navigator = () => {
return (
<Router>
<Scene key="root">
<Scene key="lauchscreen" component={LaunchScreen} hideNavBar initial />
<Scene key="feedscreen" type="reset" hideNavBar component={Feed} />
</Scene>
</Router>
);
};
export default Navigator;
now in your App.js file add this:
import Navigator from './src/Navigator.js';
export default class App extends Component<Props> {
render() {
return (
<Navigator />
);
}
}
now in your login.js when you click on login button write this:
import { Actions } from 'react-native-router-flux';
onLoginClick() {
Actions.feedscreen();
}
Thats it.. happy coding.
If you want to navigate to Feeds.js then navigate as
this.props.navigation.navigate('App');
not as
this.props.navigation.navigate('Main');
because your
export default SwitchNavigator({
Auth: AuthStack,
App: AppStack // here is your stack of Main
});
refer example
I came across the same issue few months ago. Thank god you have spent just 6 hours, i almost spent around 4 days in finding a solution for it.
Coming to the issue, Please note that in react-navigation you can either navigate to siblings or children classes.
So here, You have a swtichNavigator which contain 2 stack navigators (say stack 1 and stack 2), stack1 has feeds and stack2 has a tab navigator with login and signup.
Now you want to navigate from login.js to feeds.js(say file name is feeds.js). As mentioned already you can not navigate back to parent or grandparent. Then how to solve this issue?
In react native you have the privilege to pass params (screenprops) from parent to children. Using this, you need to store this.props.navigation of launchScreen into a variable and pass it to tab/login (check the tree structure). Now in the login.js use this variable to navigate.
You are simply passing the navigating privilege from parent to children.
Editing here:
<InnerTab screenProps={{rootNavigation : this.props.navigation }} />
Here, InnerTab is the tab navigator.
export const InnerTab = TabNavigator({
login: {
screen: login,
},
},
signup: {
screen: signup,
},
},
},
in login class, use const { navigate } = this.props.screenProps.rootNavigation;
Now you can use variable navigate.
I know its little tricky to understand but i have tried and it works.
Write your Navigator.js file as below,
import React from 'react'
import { NavigationContainer, useNavigation } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
const SwitchNavigatorStack = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='{nameofscreen}' screenOptions={screenOptions}>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default SwitchNavigatorStack
Once, you are done with that change your App.js file to,
import SignedInStack from './navigation'
import React from 'react'
export default function App() {
return <SwitchNavigatorStack/>
}
After this, you are done with setting your project for navigating. In all the components where you want to add navigation feature make sure you use the navigation.navigate() (or) navigation.push() method. Also make sure you hook navigation constant by import useNavigation library. For example,
const Login = () => {
const navigation = useNavigation()
< Button title = 'Login' onPress={() => navigation.navigate('{nameofscreen}')} />
}
with this code snippet you can implement navigation between screens using #react-navigation/native and #react-navigation/stack

How can I reset redux state when hitting go back in a web browser?

This is my use case:
In the WelcomeScreen I have code like this:
class WelcomeScreen extends Component {
render() {
const {
checkoutState,
} = this.props;
if (checkoutState.status === TRYING_TO_BUY) {
return this.renderPurchaseForm(plan);
}
return this.renderWelcome();
}
When the user visit /welcome, he can hit a purchase button, which will dispatch an action and set the checkoutState.status to TRYING_TO_BUY. The Welcome will be rerendered by calling renderPurchaseForm
Within renderPurchaseForm, it will render a ArticlePurchaseBlock
renderPurchaseForm() {
const { articleId } = this.props;
return (
<ArticlePurchaseBlock
articleId={articleId}
/>
)
and in the block, the class will try to update the url to reflect that it is in an input form
import { withRouter } from 'react-router';
class ArticlePurchaseBlock extends Component {
componentWillMount() {
const { history } = this.props;
history.push(URL_BUY_ARTICLE);
}
render() {
// render a redux-form
}
}
export default withRouter(ArticlePurchaseBlock);
You can see the history.push(URL_BUY_ARTICLE); is called in componentWillMount.
Now the problem is: when the user in the purchase form, if a user wants to go back to previous url (/welcome) , he can't. It is because the state of checkoutState.status is still TRYING_TO_BUY. The welcome is always rendered to the form.
Is there any where within the ArticlePurchaseBlock I can monitor the go back event and unset the state? I do not plan to use redux-saga-router yet because of time constraint.
I am using react-router v4
I designed a router for this exact problem. It's excessively difficult with react-router. https://github.com/cellog/react-redux-saga-router. For your code:
https://gist.github.com/cellog/0731f7e1ba8f9009f6b208c2bd15aa16
The entire thing can be done in 1 line of code, and your routes look almost identical to react-router, with 1 additional line for mapping param or url change to action.