How do I call the openDrawer function on DrawerLayoutAndroid from parent component? - reactjs

I have made a component which sets up the DrawerLayoutAndroid, which I want to call in my index file.
drawer.js:
export default class MenuDrawer extends Component {
constructor(props) {
super(props);
this.openDrawer = this.openDrawer.bind(this);
}
render() {
var navigationView = (
// ...
);
return (
<DrawerLayoutAndroid
ref={(_drawer) => this.drawer = _drawer}
drawerWidth={200}
drawerPosition={DrawerLayoutAndroid.positions.Left}
renderNavigationView={() => navigationView}>
{this.props.children}
</DrawerLayoutAndroid>
);
}
openDrawer() {
this.drawer.openDrawer();
}
}
I then have everything in the render() function in my index file wrapped around since I want the drawer to be accessible from anywhere. I just cannot figure out how I open the drawer from the index file. I have tried several different ways to call the function, but I always end up getting undefined is not an object.
index.android.js
export default class AwesomeProject extends Component {
constructor(props) {
super(props);
this.openMenu = this.openMenu.bind(this);
}
render() {
const { region } = this.props;
return (
<MenuDrawer
ref={(_menudrawer) => this.menudrawer = _menudrawer}
style={styles.layout}>
<TouchableHighlight
style={styles.topbar}
onPress={this.openMenu}>
<Text>Open</Text>
</TouchableHighlight>
</MenuDrawer>
);
}
openMenu() {
this.refs.menudrawer.openDrawer();
}
}
This gives me the error "undefined is not an object (evaluating 'this.refs.menudrawer.openDrawer')".
How do I go about solving this?
Thanks

It looks good, you're just accessing the menudrawer incorrectly. It should be:
this.menudrawer.openDrawer();
Because your ref is:
ref={(_menudrawer) => this.menudrawer = _menudrawer}

Related

How to get scroll properties in react-simplebar (in stateful function)

I am new with refs in react.js and in the react-simplebar documentation it just shows how to get the scroll ref for a stateless function.
class App extends React.Component {
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar ref={this.refs.scroll}><h1>scrollable element</h1></Simplebar>
)
}
}
For anyone coming to this at a later date. This is how I managed to set the scrolling position after a few hours of digging around in a function component
const SimpleScrollerComponent = () => {
// Create a reference for the SimpleBar component so we can acces it
const scrollableNodeRef = React.createRef();
const handleScrollDownBtnClicked = () => {
// This is where we set the scroll position
scrollableNodeRef.current.scrollTop = 1200;
};
return (
<div>
{/* We attach the reference to the component */}
<SimpleBar scrollableNodeProps={{ ref: scrollableNodeRef }}>
{/* This is where your code goes inside the scroll box */}
</SimpleBar>
<Button onClick={handleScrollDownBtnClicked}>Scroll to the Bottom</Button>
</div>
);
};
try this
class App extends React.Component {
constructor(props) {
super(props);
this.scrollableNodeRef = React.createRef();
}
onChangeScrollToTop() {
this.scrollableNodeRef.current.scrollTop = 0;
}
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar scrollableNodeProps={{ ref:this.scrollableNodeRef }}>
<h1>scrollableelement</h1>
</Simplebar>
)
}
}

Calling props from a container

I am a little confused on the idea of using props in the context I am using for my React app. In my component, I need to check if the value of a certain prop (props.companyCode) matches a certain string, and only then will it print out a <p> of what I need. Below is what I have for calling the prop in the component:
Components/CompanyContact.jsx
class CompanyContact extends React.Component {
help() {
if (this.props.companyInfoList.companyCode === '1234') {
return <p>something</p>;
}
return <p>somethingelse</p>;
}
render() {
const help = this.help();
return (
<div>
{help};
</div>
)}}
export default CompanyContact;
And this is what I have for the container:
Container/InfoContainer.jsx
class InfoContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
companyInfoList: null,
};
}
async componentWillMount() {
const companyInfoCachedData = CachingService.getData('companyInfoList');
if (companyInfoCachedData) {
this.setState({ companyInfoList: companyInfoCachedData });
return;
}
}
async getCompanyInfo(accessToken) {
try {
const companyProfileResponse = await requestAWSGet('api/company-profile', undefined, accessToken);
CachingService.setData('companyInfoList', companyProfileResponse);
this.setState({ companyInfoList: companyProfileResponse });
} catch (err) {
throw err;
}
}
render() {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
}
}
export default InfoContainer;
Nothing is returned when I run the application and I believe it's because I'm not calling the prop correctly in my component but I am unsure as to how to go about fixing it. I'm fairly new to working with props so still trying to get my bearings.
I'm assuming you are getting an error somewhere because of this not having props and this.props.companyInfoList.companyCode trying to access a property on a non object. this.props.companyInfoList is initially set to null so accessing a property on it will break.
A few strategies to fix the problem:
Default it to an empty object
this.state = {
companyInfoList: {},
}
Block the rendering of the component until it has a value:
if (this.state.companyInfoList) {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
} else {
return null;
}
Check that the prop is an object and has the key companyCode on it:
if (this.props.companyInfoList &&
this.props.companyInfoList.companyCode &&
this.props.companyInfoList.companyCode === '1234') {
In addition, this will be in the wrong context and the changes above will most likely no be enough. Try changing to an arrow function like this:
help = () => {
// your code here
}
I would personally refactor that component logic and directly use the prop value inside the render method like:
class CompanyContact extends React.Component {
render() {
const { companyInfoList } = this.props;
return companyInfoList && companyInfoList.companyCode === '1234' ? (
<p>something</p>
) : (
<p>somethingelse</p>
)
}
}
export default CompanyContact;

How can I correctly pass state as props from one component to another?

I'm trying to pass my state as props from component Locatione.js to Map.js, so the props are available when I call the function SendLocation in Map.js.
Here is my component Locatione
export default class Locatione extends Component {
state = {
location: null
};
componentDidMount() {
this._getLocationAsync();
}
_getLocationAsync = async () => {
let location = await Location.getCurrentPositionAsync({ });
this.setState({ location });
console.log("log this pls", this.state); // the state here logs correctly
};
render() {
return (
<Map locatione={this.state} /> // when accesing this props in Map, I'm getting **null**
);
}
}
Here is my Map.js component
export default class Map extends React.Component {
sendLocation() {
console.log("sending location log", this.props); // the props here appear as null
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, () => console.log("hi", this.props))} //the props here log correctly
/>
);
}
}
I also tried passing my props in this fashion, to no avail.
export default class Map extends React.Component {
sendLocation(altitude, longitude) {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, (this.props)))}
/>
);
}
}
Thanks for your help
There is a little problem here:
onPress={(this.sendLocation, () => console.log("hi", this.props))}
The console.log will trigger everytime the code renders or re-renders the button, not when you click it.
If you want to log after you call a function change the onPress to:
onPress={() => {
this.sendLocation()
console.log("hi", this.props)
}}
The other problem is that you are not giving your sendLocation function access to this.
You have two ways of doing it:
First way: Binding it inside your constructor. So inside your Map.js you add it like:
constructor(props){
super(props);
this.sendLocation.bind(this);
}
Second way: Declaring your sendLocation function as an arrow function:
sendLocation = () => {
console.log("sending location log", this.props);
}
Just as you can pass regular values as props, you can also grab data from a component’s state and pass it down as props for any of its child components. You just need to pass the exact value, also use constructor in case of class components.
`export default class Location extends Component {
constructor(props) {
super(props);
this.state = {
location: null
};
}
render() {
return (
<Map location={this.state.location} />
);
}
}`
You need to pass the function to onPress and use arrow function to be able to use this inside sendLocation.
class Map extends React.Component {
sendLocation = () => {
console.log('sending location log', this.props.locatione); // the props here appear as null
};
render() {
return (
<Button
title="Send Sonar"
onPress={this.sendLocation}
/>
);
}
}
You are passing the props through components correctly, but you should use arrow function and also anonymous func.
Try:
export default class Map extends React.Component {
sendLocation = (altitude, longitude) => {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={()=>this.sendLocation}
/>
);
}
}

ReactJS: TypeError: this.ref.current.method is not a function with ant design form

class Parent extends Component {
constructor(props) {
super(props);
this.Child_A = React.createRef();
this.Child_B = React.createRef();
}
function_uses_Child_A = ()=> {
// This is working fine
this.Child_A.current.Child_A_Function()
}
function_uses_Child_B = ()=> {
// This is Does NOT work
// this.Child_A.current.Child_B_Function() is not a function
this.Child_A.current.Child_B_Function()
}
render() {
return (
<div>
<Child_A ref={this.Child_A}/>
<Child_B ref={this.Child_B}/>
</div>
);
}
}
export default Parent;
The above code shows my problem where both has the same code but one works and the other doesn't
This is Child A component:
class Child_A extends Component {
Child_A_Function = () => "Working";
render = () => <h1>Child_A</h1>
}
export default Child_A;
This is Child B component:
import {Form} from "antd";
class Child_B extends Component {
Child_B_Function = () => "Not Working";
render = () => <h1>Child_B</h1>
}
export default Form.create()(Child_B);
I tried to debug this.Child_B.current
image debug info
I believe it shows the Form.create() data and removing mine
I understand this because Child_A works fine and the only different is it doesn't have Form.create()
This is because Form.create()() is a higher order function which returns another component.
so
const DecoratedChild_B = Form.create()(Child_B);
DecoratedChild_B may have other wrapper around it, and it become like this:
<wrapper ref={this.Child_B}>
<Child_B/>
</wrapper>
That's why you don't get what you want.
to get form ref you should use wrappedComponentRef
const EnhancedForm = createForm()(Form);
<EnhancedForm wrappedComponentRef={(inst) => this.formRef = inst} />
this.formRef // => The instance of Form
if you want something custom, you have to use other name for the ref func

A more elegant way of using Reactjs to redirect a page without setTimout and causing Warning: setState(...): Can only update

I have a simple react + redux login page. When auth is successful i redirect the user to home page. This initially brought up an issues of "Warning: setState(...): Can only update...". After searching around, a proposed solution was to setTimeout on the redirect function to give a small delay in order for the states to be set.
I am looking for a more elegant way to redirect a user without using setTimeout as it can be a little unpredictable depending on situation and without causing "setState(...)" warning .
Below is a condensed version of the code:
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
//...
};
}
render() {
let {isLoginSuccess } = this.props;
return (
<div><form>
//...
{isLoginSuccess && (
<div>Success {(setTimeout(() => this.goHome()), 1)}</div>
)}
</form></div>
);
}
goHome = function() {
this.props.history.push('/');
};
Since isLoginSuccess is a prop, you could check for it in componentWillReceiveProps function and redirect from there
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
//...
};
}
componentWillReceiveProps(nextProps) {
const {isLoginSuccess} = this.props;
if(!isLoginSuccess && nextProps.isLoginSuccess) {
this.props.history.push('/');
}
}
render() {
let {isLoginSuccess } = this.props;
return (
<div><form>
//...
{isLoginSuccess && (
<div>Success </div>
)}
</form></div>
);
}
If you are not using React-Router, you can rewrite your code to use other life-cycle events, specifically componentDidMount to do this. Do not cause any state changes in render.
Example:
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
//...
};
}
goHome() {
this.props.history.push('/');
}
componentDidMount(){
let {isLoginSuccess } = this.props;
if (isLoginSuccess){
this.goHome();
}
}
render() {
let {isLoginSuccess } = this.props;
return (
<div><form>
//...
{isLoginSuccess && (
<div>Logged-in successfully, please wait while we take you to home...</div>
)}
</form></div>
);
}
}

Resources