React pass child class method to parent functional component - javascript

I am trying to get adaptValue from Component1 and use it in Component2. For some reason this does not work since my adaptValue is always null/undefined. Is it because Parent is a functional component?
const Parent = (props) => {
const [adaptValue, setAdapt] = useState(null);
return (
<div>
<Component1 setAdapt={setAdapt}/>
<Component2 adaptValue={adaptValue}/>
</div>
)
}
export default class Component1 extends Component {
constructor(props) {
super(props);
}
adaptValue = (value) =>{
DO_SOMETHING_WITH_VALUE
}
componentDidMount() {
this.props.setAdapt(this.adaptValue);
}
render() {
return something;
}
}
export default class Component2 extends Component {
constructor(props) {
super(props);
}
someFunction = (value) =>{
...
//adaptValue is always undefined
this.props.adaptValue(value)
...
}
render() {
return something;
}
}
UPDATE Made the parent a class component in the end and all works. Wondering whether this is a compatibility issue between functional or class-based components.

When passing setAdapt to Component1 ... setAdapt is already a function. There is no need to wrap it in another one. Component1 will modify the value, and Component2 will display it. Function Components have nothing to do with the behavior.
Try ...
App.js
import React, { useState } from "react";
import "./styles.css";
import Component1 from "./Component1";
import Component2 from "./Component2";
export default function App() {
const [adaptValue, setAdapt] = useState(null);
return (
<div>
<Component1 setAdapt={setAdapt} />
<Component2 adaptValue={adaptValue} />
</div>
);
}
Component1.js
import React, { Component } from "react";
export default class Component1 extends Component {
handleClick = () => {
this.props.setAdapt("New Value");
};
render() {
return <button onClick={() => this.handleClick()}>Set Value</button>;
}
}
Component2.js
import React, { Component } from "react";
export default class Component2 extends Component {
render() {
return !!this.props.adaptValue ? (
<h1>{`"${this.props.adaptValue}" <- Value of adaptValue`}</h1>
) : (
<h1>adaptValue Not Assigned</h1>
);
}
}
Sandbox Example ...

Related

Using button in one component to render another component in main (App) component

I am trying to display/hide one component which is ItemMain and which is imported to the main App component using button in another component which is NavLogoNew. I tried to do this in many different ways but it looks like the button doesn't know if it's clicked, when I change true/false manually it works. In web I found a lot of stuff about situations when only two components are involved, but nothing like this. My code:
App
import React from 'react';
import './App.css';
import { tsPropertySignature } from '#babel/types';
import { statement } from '#babel/template';
import NavBar from './../Components/Navigation/NavBar/NavBar.js';
import ItemMain from './../Components/Item/ItemMain/ItemMain.js';
import ItemList from './../Components/Item/ItemList/ItemList.js';
import NavButtonTop from './../Components/Navigation/NavButton/NavButtonTop/NavButtonTop.js';
import NavLogoNew from './../Components/Navigation/NavButton/NavButtonNew/NavLogoNew.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
visible: !this.visible
})
}
render() {
return (
<div className="App">
<NavBar />
{this.state.visible ? <ItemMain /> : null}
<ItemList />
<NavButtonTop name='UP'/>
</div>
);
}
}
export default App;
NavLogoNew:
import React from 'react';
import './NavLogoNew.css';
import ItemMain from './../../../Item/ItemMain/ItemMain.js'
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={this.props.click}
>
{this.props.name}
</button>
);
}
}
export default NavLogoNew;
Your handleClick function is lacking something
use !this.state.visible so change from the below
handleClick(){
this.setState({
visible: !this.visible
})
}
to
handleClick = () => {
this.setState({
visible: !this.state.visible
})
}
pass the handleClick function to the NavLogoNew as follows
<NavLogoNew onClick = {this.handleClick} />
inside of the NavLogoNew component you should invoke it as follows
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={() => this.props.onClick()}
>
{this.props.name}
</button>
);
}
}

How to change className?

So I keep div element in my state. I want to change it's className in response to onClick event. I know I could do it with event.target.className but the code below is only the sample of a biggest application and it's not possible to use it there. As a resultant from changeClass function I get
"TypeError: Cannot assign to read only property 'className' of object '#'".
So I wonder is there any other way to do it?
import React, { Component } from "react";
import "./styles/style.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
myDiv: [
<div
id="firstDiv"
key={1}
className={"first"}
onClick={this.changeClass}
/>
]
};
}
changeClass = () => {
this.setState(prevState => {
return { myDiv: (prevState.myDiv[0].props.className = "second") };
});
};
render() {
return <div>{this.state.myDiv.map(div => div)}</div>;
}
}
export default App;
Don't put your jsx in state. only add className and state and onChangeClass use this.stateState to update className.
import React, { Component } from "react";
import "./styles/style.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
className:"first"
};
}
changeClass = () => {
this.setState({ classNmae: "two" });
};
render() {
return <div>
<div
id="firstDiv"
className={this.state.className}
onClick={this.changeClass}
/>
</div>;
}
}
export default App;
there's a simpler option try this:
import React, { Component } from "react";
import "./styles/style.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
className: "first"
};
}
changeClass = () => {
this.setState({className: "second"});
};
render() {
return <div
id="firstDiv"
className={this.state.className}
onClick={this.changeClass}>
</div>;
}
}
export default App;
You can use Hooks if you use a React version upper than 16.8
import React, { useState } from "react"
import "./styles/style.css"
const App = () => {
const [myClass, setMyClass] = useState("first")
const changeClass = () => {
setMyClass("second")
}
render() {
return <div
id="firstDiv"
className={myClass}
onClick={changeClass}>
</div>;
}
}
export default App

Can i pass component state to HoC?

Is there any way to send data from the component's state to HoC?
My component
import React, { Component } from 'react';
import withHandleError from './withHandleError';
class SendScreen extends Component {
contructor() {
super();
this.state = {
error: true
}
}
render() {
return (
<div> Test </div>
)
}
};
export default withHandleError(SendScreen)
My HoC component:
import React, { Component } from 'react';
import { ErrorScreen } from '../../ErrorScreen';
import { View } from 'react-native';
export default Cmp => {
return class extends Component {
render() {
const { ...rest } = this.props;
console.log(this.state.error) //// Cannot read property 'error' of null
if (error) {
return <ErrorScreen />
}
return <Cmp { ...rest } />
}
}
}
Is there any way to do this?
Is the only option is to provide props that must come to the SendScreen component from outside??
A parent isn't aware of child's state. While it can get an instance of a child with a ref and access state, it can't watch on state updates, the necessity to do this indicates design problem.
This is the case for lifting up the state. A parent needs to be notified that there was an error:
export default Cmp => {
return class extends Component {
this.state = {
error: false
}
onError() = () => this.setState({ error: true });
render() {
if (error) {
return <ErrorScreen />
}
return <Cmp onError={this.onError} { ...this.props } />
}
}
}
export default withHandleError(data)(SendScreen)
In data you can send the value you want to pass to HOC, and can access as prop.
I know I answer late, but my answer can help other people
It is very easy to do.
WrappedComponent
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import HocComponent from './HocComponent';
const propTypes = {
passToHOC: PropTypes.func,
};
class WrappedComponent extends Component {
constructor(props) {
super(props);
this.state = {
error: true,
};
}
componentDidMount() {
const {passToHOC} = this.props;
const {error} = this.state;
passToHOC(error); // <--- pass the <<error>> to the HOC component
}
render() {
return <div> Test </div>;
}
}
WrappedComponent.propTypes = propTypes;
export default HocComponent(WrappedComponent);
HOC Component
import React, {Component} from 'react';
export default WrappedComponent => {
return class extends Component {
constructor() {
super();
this.state = {
error: false,
};
}
doAnything = error => {
console.log(error); //<-- <<error === true>> from child component
this.setState({error});
};
render() {
const {error} = this.state;
if (error) {
return <div> ***error*** passed successfully</div>;
}
return <WrappedComponent {...this.props} passToHOC={this.doAnything} />;
}
};
};
React docs: https://reactjs.org/docs/lifting-state-up.html
import React, { Component } from 'react';
import withHandleError from './withHandleError';
class SendScreen extends Component {
contructor() {
super();
this.state = {
error: true
}
}
render() {
return (
<div state={...this.state}> Test </div>
)
}
};
export default withHandleError(SendScreen)
You can pass the state as a prop in your component.

How to use same data in two different .js file (React Native)

In the js file below we create an integer(ttSelectedItem).
How do you use it on another .js file ?
(Without clicking any button)
Is AsyncStorage solving that problem? If it is true, how?
import React, { Component } from 'react';
import {Platform,StyleSheet,Text,View,Image,ImageBackground} from 'react-native';
import Picker from 'react-native-wheel-picker'
var PickerItem = Picker.Item;
var numberList = [];
var ttSelectedItem,
for (let i = 0; i < 41; i++){
numberList.push(i.toString());
}
export default class yks extends Component<{}> {
constructor(props) {
super(props);
this.state = {
ttSelectedItem : 20,
itemList: numberList,
};
}
onPickerSelect (index, selectedItem) {
this.setState({
[selectedItem] : index,
})
}
render () {
return (
<View>
<Picker style={{width: "100%", height: "100%"}}
selectedValue={this.state.ttSelectedItem}
onValueChange={(index) => this.onPickerSelect(index, 'ttSelectedItem')}>
{this.state.itemList.map((value, i) => (
<PickerItem label={value} value={i} key={"money"+value}/>
))}
</Picker>
</View>
);
}
}
You can create a file ttSelectedItem.js and import it in all the components you need.
Example:
//ttSelectedItem.js
const ttSelectedItem = 'Hello';
export default ttSelectedItem
//YourComponent.js
import ttSelectedItem from './path-to-ttSelectedItem';
class YourComponent extends React.Component {
console.log(ttSelectedItem); // print Hello
}
More info: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
You can also pass down the prop from a parent component to its children.
Example:
// App.js
import FirstComponent from 'path-to-first-component';
import SecondComponent from 'path-to-second-component';
class App extends React.Component {
render() {
return (
<View>
<FirstComponent ttSelectedItem={'Hello'} />
<SecondComponent ttSelectedItem={'Hello'} />
</View>
)
}
}
// FirstComponent.js
class FirstComponent extends React.Component {
console.log(this.props.ttSelectedItem) //print Hello
}
export default FirstComponent
// SecondComponent.js
const SecondComponent = (props) => {
console.log(props.ttSelectedItem) //print Hello
}
export default SecondComponent
Depending on how complex your code will be, you can use HOCs to wire up some data and pass down your components
Example:
//ttSelectedItem.js
const ttSelectedItem = (Component) => {
return <Component ttSelectedItem={'Hello'} />
}
export default ttSelectedItem;
//YourComponent.js
import ttSelectedItem from 'path-to-ttSelectedItem';
class YourComponent extends Component{
(...)
console.log(this.props.ttSelectedItem); //print Hello
(...)
}
export default ttSelectedItem(YourComponent);
More detail: https://reactjs.org/docs/higher-order-components.html
Or if you need an even complex code, you can use Redux Store to keep this data
Example using Redux and ReduxThunk:
//App.js
import ReduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reducer from 'path-to-your-reducer';
import YourComponent from 'path-to-your-component';
class App extends Component {
render() {
const store = createStore(reducer, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<YourComponent />
</Provider>
);
}
}
// YourComponent.js
import { connect } from 'react-redux';
class YourComponent extends React.Component {
console.log(this.props.ttSelectedItem) // prints Hello
}
const mapStateToProps = function(state){
return {
ttSelectedItem: state.ttSelectedItem,
}
}
export default connect(mapStateToProps, {})(MainAppContainer)
// Reducer.js
const INITIAL_STATE = {
ttSelectedItem: 'Hello',
};
export default (state = INITIAL_STATE) => {
return state;
};
More info: https://redux.js.org/basics/store
The last example is just to show another way to handle data between components using Redux. It should be used only when dealing with really complex data sharing.
I'd suggest you to just follow the first example, it might be enough
Hope it helps

React.js - How to implement a function in a child component to unmount another child from the same parent, and mount another component on it's place?

For example, a component like this:
import React, { Component } from 'react';
import BodyContent from './BodyContent';
import BottomOne from './BottomOne';
import BottomTwo from './BottomTwo';
class App extends Component {
render() {
return (
<div className="App">
<BodyContent />
<BottomOne />
</div>
);
}
}
export default App;
I want to implement a function on BodyContent that unmount BottomOne and mounts BottomTwo instead, so when I activate the function, the code is reestructured to this:
import React, { Component } from 'react';
import BodyContent from './BodyContent';
import BottomOne from './BottomOne';
import BottomTwo from './BottomTwo';
class App extends Component {
render() {
return (
<div className="App">
<BodyContent />
<BottomTwo />
</div>
);
}
}
export default App;
I'm very new to React, so if there's a better way to do it, I'm open to suggestions, but I really need that end result, a function on BodyContent that unmounts BottomOne and mounts BottomTwo.
You can maintain a state which tells which component to render. Something roughly like this
import React, { Component } from 'react';
import BodyContent from './BodyContent';
import BottomOne from './BottomOne';
import BottomTwo from './BottomTwo';
class App extends Component {
changeBottomComponent = (comp) => {
this.setState({ showBottom: comp})
}
render() {
return (
<div className="App">
<BodyContent changeBottomComponent={this.changeBottomComponent}/>
{this.state.showBottom === 1 ? <BottomOne /> : <BotttomTwo />}
</div>
);
}
}
export default App;
To achieve that maintain a state variable in parent component (some kind of identifier for component) and use that state variable to render different component.
Along with that you also need to pass a function from parent to child and use that function to update the parent state value.
Like this:
class App extends Component {
constructor(){
super();
this.state={
renderOne: true,
}
this.update = this.update.bind(this);
}
update(){
this.setState({renderOne: false})
}
render() {
return (
<div className="App">
<BodyContent update={this.update}/>
{this.state.renderOne? <BottomOne /> : <BottomTwo/> }
</div>
);
}
}
Now inside BodyContent component call this.props.update() to render another component.
You can use state or props to render different components.
Example:
import React, {
Component
}
from 'react';
import BodyContent from './BodyContent';
import BottomOne from './BottomOne';
import BottomTwo from './BottomTwo';
class App extends Component {
constructor(props) {
super(props);
this.state = {
decider: false
};
}
render() {
const bottomContent = this.state.decider === true ? <BottomOne /> : <BottomTwo />;
return (
<div className="App">
<BodyContent />
{ bottomContent }
</div>
);
}
}
export
default App;
You can also directly use the components in the state and render them. Could be more flexible this way.
const BottomOne = () => <div>BottomOne</div>;
const BottomTwo = () => <div>BottomTwo</div>;
class Example extends React.Component {
constructor() {
super();
this.state = { show: BottomOne };
this.toggleComponent = this.toggleComponent.bind(this);
}
toggleComponent() {
// Use whatever logic here to decide.
let show = BottomOne;
if (this.state.show === BottomOne) {
show = BottomTwo;
}
this.setState({ show });
}
render() {
return (
<div>
<button onClick={this.toggleComponent}>Change</button>
<this.state.show />
</div>
);
}
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Categories

Resources