How to make component re-render when value from outside changes? - javascript

So, I have simple code (class) like this:
export default class LoginAction {
isLoggedIn = () => {
return true
}
}
And I used it in my other classes like this:
export default class Main extends Component {
render = () => {
const loginAction = new LoginAction()
if (loginAction.isLoggedIn()) {
return (
<View style={{ flex: 1 }}>
<Header headerText={'Post List'} />
<PostList />
</View>
)
}
....... (split)
}
}
The question is, when I change the return value on the isLoggedIn function, why Main component not re-rendered?
It's React Native, and I use Hot Reloading.

A component re-renders only in 2 situations:
if its state has changed
if the received props have changed
In your Main component, none of these situations happen.
To fix it, you could pass isLoggedIn to your component:
// index.js
const loginAction = new LoginAction()
let isLoggedIn = loginAction.isLoggedIn()
const setLoggedUser = user => {
loginAction.setLoggedUser(user)
isLoggedIn = true
}
ReactDOM.render(
<div>
{!isLoggedIn && <Login setLoggedUser={setLoggedUser} />}
<Main isLoggedIn={isLoggedIn} />
</div>,
document.getElementById('root')
)
And use this prop in your component's render:
export default class Main extends Component {
render = () => {
if (this.props.isLoggedIn) {
return (
<View style={{ flex: 1 }}>
<Header headerText={'Post List'} />
<PostList />
</View>
)
}
...
}
}
In doing so, your component will re-render when isLoggedIn changes.

Related

using If/Else statement in ReactJs to return a component

Here is courseButton.jsx:
import React from "react";
import styles from "./styles.module.scss";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "#ant-design/icons";
export default (props) => {
const { collapsed, onClick } = props;
return <>{collapsed ? MenuUnfoldOutlined : MenuFoldOutlined}</>;
};
Both of my components have the same props. So I want to avoid coding like this:
import React from "react";
import styles from "./styles.module.scss";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "#ant-design/icons";
export default (props) => {
const { collapsed, onClick } = props;
return (
<>
{collapsed ? (
<MenuUnfoldOutlined className={styles.trigger} onClick={onClick} />
) : (
<MenuFoldOutlined className={styles.trigger} onClick={onClick} />
)}
</>
);
};
So how I can give the selected component the style in one line code.
I want something like this code.
This solution scales better as we assign props only once.
export default (props) => {
const { collapsed, onClick } = props;
const Component = collapsed ? MenuUnfoldOutlined : MenuFoldOutlined;
return <Component className={styles.trigger} onClick={onClick} />;
};
If I understood you correctly, you want to keep your code DRY. You can store your props in a variable to keep it reusable.
export default (props) => {
const {collapsed, onClick} = props;
const genericProps = {
className: styles.trigger,
onClick,
}
if (collapsed) {
return <MenuUnfoldOutlined {...genericProps} />
}
return <MenuFoldOutlined {...genericProps} />
}
Note: React Fragment is redundant.
you can simply write it like this:
import React from "react"
import styles from "./styles.module.scss"
import {MenuFoldOutlined, MenuUnfoldOutlined} from '#ant-design/icons'
export default (props) => {
const {collapsed, onClick} = props
return (
<>
{collapsed ? <MenuUnfoldOutlined {...props} /> : <MenuFoldOutlined {...props}/>}
</>
)
}
This is the same as writing .
If the wrapping component has a bunch of props and you only need specific props you can try the following approach:
export default (props) => {
// If props has a bunch of props but we only need collapsed and
// onClick:
const {collapsed, onClick} = props
const menuProps = {collapsed, onClick}
return (
<>
{collapsed ? <MenuUnfoldOutlined {...menuProps} /> : <MenuFoldOutlined {...menuProps}/>}
</>
)
}

How to use HoC with React Native

I have an listing app where users can add items for multiple categories, when they want to add new record, there are 3 related screens with this particular feature. All of those screens have <Header/> component, so i thought HoC would be nice here so that i can reuse it across 3 screens.
However, i could not accomplish it.
Here is what i tried so far:
This is my HoC class
import React, { Component } from 'react';
import { View, StyleSheet, Text, StatusBar } from 'react-native';
import Header from '../components/Header';
const NewAd = (WrappedComponent) => {
class NewAdHoc extends Component {
handleBackPress = () => {
this.props.navigation.navigate('Home');
StatusBar.setBarStyle('dark-content', true);
}
render() {
const {contentText, children} = this.props
return (
<View style={styles.container}>
<Header
headerText={'Yeni ilan ekle'}
onPress={this.handleBackPress}
/>
<View style={styles.contentContainer}>
<Text style={styles.contentHeader}>{contentText}</Text>
<WrappedComponent/>
</View>
</View>
);
}
}
return NewAdHoc;
}
this is my screen:
class NewAdScreen extends Component {
render() {
const Content = () => {
return (
<View style={styles.flatListContainer}>
<ListViewItem />
</View>
);
}
return (
NewAdHoc(Content)
)
}
}
after that i am getting error
TypeError: (0 , _NewAdHoc.NewAdHoc) is not a function(…)
and i have no idea how can i fix it because this is my first time using hocs on a react-native app. I have looked why this error is popping and they suggest import components in this way:
import {NewAdHoc} from '../hocs/NewAdHoc';
but even this is not solved it.
any help will be appreciated, thanks.
The main purpose of a HOC is to encapsulate and reuse stateful logic across components. Since you are just reusing some jsx and injecting nothing in WrappedComponent you should be using a regular component here:
const NewAd = ({ contentText, children }) => {
handleBackPress = () => {
this.props.navigation.navigate('Home');
StatusBar.setBarStyle('dark-content', true);
}
return (
<View style={styles.container}>
<Header
headerText={'Yeni ilan ekle'}
onPress={this.handleBackPress}
/>
<View style={styles.contentContainer}>
<Text style={styles.contentHeader}>{contentText}</Text>
{children}
</View>
</View>
);
}
And use it like this
return(
<>
<NewAd>
<Screen1 />
</NewAd>
<NewAd>
<Screen2 />
</NewAd>
<NewAd>
<Screen3 />
</NewAd>
</>
)

react - How to open show more on a function component?

I have a functional component
const text = ({data}) => {
return (
<p onClick={()=> render more?}>info</p>
)}
const more = ({data}) => {
return (<p>..........</p>)
}
Is it possible to render more component on the onClick event?
Sure, you'll need a state variable. Use the state to determine whether to render more or not, and then set the state when the click happens. If you have react 16.8 or later, you can do this in a functional component with hooks:
import { useState } from 'react';
const MyComponent = ({data}) => {
const [showMore, setShowMore] = useState(false);
return (
<div>
<p onClick={() => setShowMore(true)}>info</p>
{showMore && <More data={data} />}
</div>
)}
}
Prior to 16.8, you'll need to use a class component.
class MyComponent extends React.Component {
state = {
showMore: false,
}
render() {
return (
<div>
<p onClick={() => this.setState({ showMore: true})}>info</p>
{this.state.showMore && <More data={this.props.data} />}
</div>
)}
}
}

How to forward ref in a HOC in React?

I am using Context API to use themes in my React Native project. To consume the Theme Context, I made a Higher Order Component and passed them into the component as props.
This worked fine for the most of the app. But when I started using refs, it started crashing, because as per the documentation, the refs will not be forwarded automatically, and we need to use the React.forwardRef API. But something when wrong with my implementation and I am not able to resolve it.
Here's the code I have been working on:
// Higher order component, to wrap my component with theme (withTheme.js)
export const withTheme = (Component, areRefsUsed = false) => {
// Logic to check if refs are being used
if (areRefsUsed) {
const ThemedComponent = (props, ref) => {
return (
<ThemeContext.Consumer>
{theme => (<Component {...props} theme={theme} ref={ref} />)}
</ThemeContext.Consumer>
)
};
return React.forwardRef(ThemedComponent);
}
return (props) => {
return (
<ThemeContext.Consumer>
{theme => <Component {...props} theme={theme} />}
</ThemeContext.Consumer>
)
}
};
// My component where I am using refs (ExampleComponent.js)
class ExampleComponent extends Component {
constructor(props) {
super(props);
this.refForSomeComponent = React.createRef();
}
render() {
const { theme } = this.props;
return (
<Fragment>
<Text style={{color: theme.primaryColor}}>It is working</Text>
<TextInput ref={this.refForSomeComponent} value={'Test'} />
</Fragment>
)
}
}
export default withTheme(ExampleComponent, true);
When I try to run the app, this error is being thrown:
The component for route 'ExampleComponent' must be a React component.

How to share or to recive some data from second screen In React Native

I have 2 screens, my Home Screen
class Home extends Component {
constructor(props) {
super(props)
this.state = {
myDebts: 745.8455656,
debts: 1745.54555
}
}
addFriendsHandler = () => {
Alert.alert('You tapped the button!')
}
render () {
return (
<View style={{flex: 1}}>
<Header
text={"Splitwise"} />
<Debts
myDebts={this.state.myDebts}
debts={this.state.debts}/>
<Buttons text={"+ ADD FRIENDS ON SPLITWISE"}
clicked={() => this.props.navigation.navigate("AddFriend")}/>
</View>
)
}
}
export default Home
and my second Screen
class AddFriendPage extends Component{
state = {
name: ''
}
addFriendHandler = () => {
this.props.navigation.navigate("MainPage")
}
render() {
return (
<View>
<Header text={"Add a friend"}/>
<Sae
label={'Your friends name'}
labelStyle={{ color: '#47AE4f' }}
iconClass={FontAwesomeIcon}
iconName={'pencil'}
iconColor={"#47AE4f"}
inputStyle={{ color: '#000' }}
onBlur={(e) => this.setState({name: e.nativeEvent.text})}
/>
<Buttons text={"+ ADD FRIEND"}
disable={this.state.name === ''}
clicked={this.addFriendHandler}/>
</View>
)
}
}
and my Navigator
export default class App extends React.Component {
render() {
return (
<AppStackNavigator />
);
}
}
const AppStackNavigator = createStackNavigator({
MainPage: Home,
AddFriend: AddFriendScreen
})
I want to send a function to the AddFriendPage screen from Home screen, and inside that function i want to get value from input and return the name back into Home screen, but unfortunately i have no idea how to share data between 2 screens
https://reactnavigation.org/docs/en/params.html#docsNav
You want to pass params during navigation:
() => this.props.navigation.navigate("AddFriend", {name: "Alan"})
Then in the parent method (if you want to display it, you could just put it in render):
const name = this.props.navigation.getParam(name, null)
If null, you know that the screen was reached from a different screen, and can handle that case normally. You can add whatever params you want.

Categories