react-native : Can I use props in a method? - javascript

I have my class and I have a method and I am wondering if I could use props inside a mehtod.
Notice I try to use props in methodTwo. Is this possible? If not, is there a way I could use props in method?
import React from 'react';
import { Image, Text, View } from 'react-native';
export default class Test extends React.PureComponent {
methodOne = () => {
this.setState({
one:false,
two:false,
three:false
})
}
methodTwo = () => {
this.setState({
one:false,
two:false,
//I want to use props
three:this.props.three
})
}
render() {
return (
<View style={{ backgroundColor: 'transparent', alignItems: 'center' }}>
<Button title='one' onPress={()=>this.methodOne()}/>
// I could call i like this?
<Test three='newState'/>
</View>
);
}
}

methodTwo = () => {
this.setState({
one:false,
two:false,
three:this.props.three
})
}
props-> is the value that is been transferred from parent component to child component.
In class based component you fetch the value by using this.props.Attribute_name and in functional based component you can fetch the value using props.Attribute_name (mind functional based component dont have any concept of this)
if you want to use this.props.three ,then in parent component call (the component calling this particular component) <Test three="anyValue" /> then you can easily get this value in child component.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
We could just swap out the <p> for a <Cat> here ... but then
we would need to create a separate <MouseWithSomethingElse>
component every time we need to use it, so <MouseWithCat>
isn't really reusable yet.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}

The props are accessible to whole of the class scope with the syntax this.props.xxxx if you have passed it from its parent component. SO you can use in methodOne too.

You can use props inside a method. Any specific error you are facing ?.

Related

Passing a variable between non-nested components using Context API

Suppose I have two components which aren't nested: a button and a panel. When the button is clicked, the panel will show or hide depending on the previous state (like an on/off switch). They aren't nested components, so the structure looks like this:
<div>
<Toolbar>
<Button />
</Toolbar>
<Content>
...
<ButtonPanel />
</Content>
</div>
I can't change the structure of the DOM. I also can't modify any other component other than the button and panel components.
The Button and ButtonPanel components are related, however, and will be used together throughout the solution. I need to pass a property to the panel to let it know when to show or when to hide. I was thinking about doing it with Context API, but I think there's something I'm doing wrong and the property never updates.
This is my code:
Context
import React from 'react';
export const ButtonContext = React.createContext({
showPanel: false,
});
Button
import React, { Component } from 'react';
import { ButtonContext } from './ButtonContext';
class Button extends Component {
constructor() {
super();
this.state = {
showPanel: false,
};
}
render() {
return (
<ButtonContext.Provider value={{ showPanel: this.state.showPanel }}>
<li>
<a
onClick={() => this.setState({ showPanel: !this.state.showPanel }, () => console.log('Changed'))}
>
<span>Button</span>
</a>
</li>
</ButtonContext.Provider>
);
}
}
export { Button };
Panel
import React, { Component } from 'react';
import { Panel, ListGroup, ListGroupItem } from 'react-bootstrap';
import { ButtonContext } from './ButtonContext';
class ButtonPanel extends Component {
static contextType = ButtonContext;
render() {
return (
<ButtonContext.Consumer>
{
({ showPanel }) => {
if (showPanel) {
return (
<Panel id="tasksPanel">
<Panel.Heading >Panel Heading</Panel.Heading>
<ListGroup>
<ListGroupItem>No Items.</ListGroupItem>
</ListGroup>
</Panel>
);
}
return null;
}
}
</ButtonContext.Consumer>
);
}
}
export { ButtonPanel };
I've also tried simply accessing the context in the ButtonPanel component like so:
render() {
const context = this.context;
return context.showPanel ?
(
<Panel id="tasksPanel">
<Panel.Heading >Tasks</Panel.Heading>
<ListGroup>
<ListGroupItem className="tasks-empty-state">No tasks available.</ListGroupItem>
</ListGroup>
</Panel>
)
:
null;
}
What am I doing wrong?
Thanks
From the React docs:
Accepts a value prop to be passed to consuming components that are descendants of this Provider.
So this means that <ButtonContext.Provider> has to wrap <ButtonContext.Consumer> or it has to be higher up in the component hierarchy.
So based on your use case, you could do:
// This app component is the div that wraps both Toolbar and Content. You can name it as you want
class App extends Component {
state = {
showPanel: false,
}
handleTogglePanel = () => this.setState(prevState => ({ togglePanel: !prevState.togglePanel }));
render() {
return (
<ButtonContext.Provider value={{ showPanel: this.state.showPanel, handleTogglePanel: this.handleTogglePanel }}>
<Toolbar>
<Button />
</Toolbar>
<Content>
<ButtonPanel />
</Content>
</ButtonContext.Provider>
);
}
}
class Button extends Component {
...
<ButtonContext.Consumer>
{({ handleTogglePanel }) => <a onClick={handleTogglePanel} />}
</ButtonContext.Consumer>
}
class ButtonPanel extends Component {
...
<ButtonContext.Consumer>
{({ showPanel }) => showPanel && <Panel>...</Panel>}
</ButtonContext.Consumer>
}

React State listen for Object Attribute Change

One of my components instantiates a class and stores it in the state. The Person class is used in many components.
// Car class
this.state = {
driver: new Person(),
passenger: new Person()
}
It then displays the location of the person
// Car class
render() {
return (
<div>
<img src={this.state.driver.src} style={{left: this.state.driver.x, top: this.state.driver.y}} />
<img src={this.state.passenger.src} style={{left: this.state.passenger.x, top: this.state.passenger.y}} />
<span onClick={this.state.driver.moveRight}>Move Driver</span>
<span onClick={this.state.passenger.moveRight}>Move Passenger</span>
</div>
)
}
The moveRight function being
//Person class
moveRight() {
this.x += 1;
}
This avoids re-writing the Person.moveRight() method in all the components that need the person to be moved.
However, the state does not update when the car's attributes are updated, is there a way to fix this so the state updates appropriately, or is this not a good way to implement this?
If you're considering to use a Function Component instead, this seems to be a perfect use case for implementing a custom hook.
Create a usePerson hook and then use it in your Car Component. Here, give this a try:
import React, { useState } from "react";
function usePerson() {
const [person, setPerson] = useState({
x: 0,
y: 0,
src: "https://cdn2.iconfinder.com/data/icons/rcons-car/512/car-128.png"
});
function moveRight() {
setPerson({
...person,
x: person.x + 10
});
}
return [person, moveRight];
}
export default usePerson;
And in your Car Component:
import React, { Fragment } from "react";
import usePerson from "./usePerson";
function Car(props) {
const [driver, moveRightDriver] = usePerson();
const [passenger, moveRightPassenger] = usePerson();
// console.log("Driver: ", driver);
// console.log("Passenger: ", passenger);
return (
<Fragment>
<img
alt="Driver"
src={driver.src}
style={{ left: driver.x, top: driver.y }}
/>
<img
alt="Passenger"
src={passenger.src}
style={{ left: passenger.x, top: passenger.y }}
/>
<button onClick={moveRightDriver}>Move Driver</button>
<button onClick={moveRightPassenger}>Move Passenger</button>
</Fragment>
);
}
export default Car;
Here's a Working CodeSandbox Sample for your ref.
NOTE: The images don't seem to move but if you un-comment the console.logs, you'll see that the x is indeed changing. So you might want to rectify that logic.
You could a higher-order component paradigm. This proposal does not exactly match the UI you attempting to achieve but gets you closer to your desired functionality.
Notably, I would have each Person component (by way of the HOC paradigm) manage its own state rather than have that state managed by component in which it is rendered (i.e., Car).
const withMovement = RenderComponent => {
return class extends Component {
constructor() {
super(props);
this.state = { x = 0 }
}
moveRight = () => {
const { x } = this.state;
this.setState({ x: x + 1 });
}
render() {
return <RenderComponent moveRight={this.moveRight} xPosition={this.state.x} {...this.props} />
}
}
}
class Person extends Component {
constructor() {
super(props);
// handle additional state here
}
render() {
// Use xPosition props for display
console.log('this.props.xPosition', this.props.xPosition);
return (
<button onClick={() => this.props.moveRight()} />
)
}
}
const Passenger = withMovement(Person);
const Driver = withMovement(Person);
class Car extends Component {
constructor() {
super(props);
}
render() {
return (
<Passenger />
<Driver />
)
}
}

Correctly push to an array that has been passed as props

I Defined an array.
Then passed the array to the Console child as props.
main.js
class Main extends Component {
constructor() {
super();
this.moduleArray = [];
this.state = {
moduleArray: this.moduleArray
};
}
render() {
return (
<div id="builder-root">
<Console moduleArray={this.moduleArray} />
</div>
);
}
}
}
export default Main;
I assigned the array to a const
Then onClick I am passing a parameter to the append function which runs a switch statement. Then I push the content of the matching case to the array.
console.js
class Console extends React.Component {
render() {
const { moduleArray } = this.props;
const append = x => e => {
switch (x) {
case 0:
console.log("case0");
moduleArray.push(
<div
key={moduleArray.length}
id={moduleArray.length}
style={{ fontSize: 0, lineHeight: 0 }}
>
<Mod1 />
</div>
);
console.log("pushed");
break;
//other switch cases
default:
}
this.setState({
moduleArray: this.moduleArray
});
};
return (
<div id="console">
<input onClick={append(0)} value="Single col" type="submit" />
//other clicks passing parameters
</div>
);
}
}
export default Console;
And..... nothing happens. Well I say nothing. The function runs and prints the console logs out and I don't get an error. But the content doesn't render.
I think I need to use the spread operator in some way but I am unsure and struggling to find any reading material on a scenario like this.
The Console component only renders, if its props or state is changing. Props can only be changed by the parent of the component. State is a component internal object which is changed by the component itself (but might depend on props or other calculations).
And Props are immutable. This means you can't overwrite them by
moduleArray.push(
<div
key={moduleArray.length}
id={moduleArray.length}
style={{ fontSize: 0, lineHeight: 0 }}
>
<Mod1 />
</div>
);
You even declared moduleArray as const. Change moduleArray in the parent of your component (through callback function) or initialize the state with the component's props and change the state with this.setState(/*...*/).
A possible solution is listed below:
class Main extends Component {
constructor(props) {
super(props);
this.state = {
moduleArray: [] // initialize empty or use props to init state
};
}
componentDidMount() {
// example
// this.setState({moduleArray: serverResponse.modules}
}
render() {
return (
<div id="builder-root">
<Console moduleArray={this.state.moduleArray} addModule={(module) => this.setState({
moduleArray: [
...this.state.moduleArray,
module
]
})}/>
</div>
);
}
}
}
export default Main;
class Console extends React.Component {
render() {
const {
moduleArray,
addModule
} = this.props;
const append = x => e => {
switch (x) {
case 0:
addModule(
<div
key={moduleArray.length}
id={moduleArray.length}
style={{fontSize: 0, lineHeight: 0}}
>
<Mod1/>
</div>
);
break;
//other switch cases
default:
}
};
return (
<div id="console">
<input onClick={append(0)} value="Single col" type="submit"/>
{/*other clicks passing parameters*/}
</div>
);
}
}
export default Console;

onClick event of child component is not getting triggered for stacked parent and child component

I have the following two components with thier respective onClick() handlers. Problem is doSomethingElse is never getting and directly doSomething is getting called.
<ParentComponent onClick={this.doSomething}>
<ChildComponent onClick={this.doSomethingElse} />
</Parent>
Child is shown as like proover on parent using z-index.
class Parent extends React.Component {
constructor(props) {
super(props);
}
handleP = (e) => {
alert("parent")
};
render() {
return (
<div onClick={this.handleP} style={{
left: 100,
top: 100,
zIndex: 100,
position: 'absolute',
}}>
Parent Text
<Child />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
}
handleC = (e) => {
alert("child")
};
render() {
return (
<div onClick={this.handleC} style={{
left: 1,
top: 1,
zIndex: 1000,
position: 'absolute',
}}>
Child Text
</div>
)
}
}
React.render(
<Parent />,
document.getElementById('react_example')
);
Jsbin
#SandipPingle the issue is because, by default a click event bubbles till window. So after the child handler is invoked the event bubbles to parent component. For that you need to stop the event propogation. Refer to this question and this sandbox for a sample fixed version based on this answer :)

How can I wrap the Children of a Parent component in a HOC and render them in React?

Let me start by saying that this example is very simple and can be solved with React.cloneElement. But I want more freedom and the project will be more complex, so I'd like to find a solution.
I would also like to understand what I'm missing :/
I want to be able to augment the children of a Parent component with props and methods (hence the HOC). It would start from here:
<Parent>
<aChild />
<anotherChild />
<yetAnotherChild />
</Parent>
And this is the Parent component (called Sequence in my project), so far:
import React, { Component } from 'react';
const withNotification = handler => Component => props => (
<Component onAnimationEnd={handler} {...props} />
);
class Sequence extends Component {
constructor(props) {
super(props);
this.state = {
pointer: 0,
};
this.notifyAnimationEnd = this.notifyAnimationEnd.bind(this);
this.Children = React.Children.map(this.props.children, Child =>
withNotification(this.notifyAnimationEnd)(Child)
);
}
notifyAnimationEnd() {
// do stuff
}
render() {
return (
<div>
{this.Children.map((Child, i) => {
if (i <= this.state.pointer) return <Child />;
return <div>nope</div>;
})}
</div>
);
}
}
export default Sequence;
I get the following error:
You can play with the code here: https://codesandbox.io/s/6w1n5wor9w
Thank you for any help!
This answer will not solve your problem but maybe gives a hint why this is not possible. At first I was surprised why your code does not work, even though I'm not an experienced React developer it seems ok map this.props.children through with React.Children.map and return the desired Component with your HOC. But it did not work.
I tried to debug it a little bit and did some search. I've learned props.children actually contains the elements itself not the instances of components. Even, React.Children.map does not have any effect on this.
Here is a working snippet proves that your problem is not related with the HOC. I've used an array of components instead of mapping through props.children.
class Line1 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 1</div>;
}
}
class Line2 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 2</div>;
}
}
class Line3 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 3</div>;
}
}
const withNotification = handler => Component => props => (
<Component onAnimationEnd={handler} {...props} />
);
class Sequence extends React.Component {
constructor(props) {
super(props);
this.state = {
pointer: 0
};
this.notifyAnimationEnd = this.notifyAnimationEnd.bind(this);
this.Arr = [ Line1, Line2, Line3 ];
this.Children = this.Arr.map(Child =>
withNotification(this.notifyAnimationEnd)(Child)
);
}
notifyAnimationEnd() {
this.next();
}
next() {
// Clearly, render the next element only if there is, a next element
if (this.state.pointer >= this.Arr.length - 1) {
return;
}
this.setState({ pointer: this.state.pointer + 1 });
}
render() {
return (
<div>
{this.Children.map((Child, i) => {
if (i <= this.state.pointer) return <Child />;
return <div>nope</div>;
})}
</div>
);
}
}
ReactDOM.render(
<Sequence />,
document.getElementById("app")
);
<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="app"></div>
You are returning <Child /> instead of Child in Sequence.js render method. Here is my edited copy - codesandbox

Categories

Resources