The react navigation documentation is very clear on how to pass props to functional components, but what about class-based components?
This is a screen that scans a QR code and passes the data to a class-based component
export const ScreenQRCodeScanner = ({ navigation }) => {
...
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
alert(`Bar code with type ${type} and data ${data} has been scanned!`);
var clientID = {
ClientID: data,
};
navigation.navigate("QRCodeResult", clientID);
};
...
};
QRCodeResult is this function:
export class QRCodeResult extends Component {
constructor() {
super();
this.state = {
loading: false,
clientInfo: {},
};
}
componentDidMount() {
this.setState({ loading: true });
// API call with a dynamic body based on data passed from the previous screen (clientID)
}
render() {
return (
<SafeAreaView>
<Text>Test QR Code Result Page {this.state.clientInfo.FullName}</Text>
<Text>{this.state.loading}</Text>
</SafeAreaView>
);
}
}
I am using react navigation v5 and I want to use the data passed on componentDidMount() function where I make an API call
You can access the navigation prop like below
this.props.navigation.navigate("QRCodeResult", {clientID});
And if the receiving component is also a class component you can retrieve it like below.
this.props.route.params.clientID
Related
so I am trying to pass params using route to a react component and also at the same time use Component class props. Here is what am doing
import { loadSchemes, } from '../../actions/schemes;
export class Schemes extends Component {
constructor(props) {
super(props);
const { match: { params } } = this.props;
this.state = {
client_id: params.pk,
}
}
componentDidMount() {
this.props.loadSchemes();
}
render(){
return(
<div>
{this.props.schemes_list.map((scheme,index)=><p key={index}>{scheme}</p>)}
</div>
)
}
}
const mapStateToProps = (state) => ({
schemes_list: state.schemes,
});
export default connect(mapStateToProps,{ loadSchemes,})(Schemes);
And I have a url to this component as
<Route path="/client/:pk/schemes" component={Schemes}/>
The problem is I get an error this.props.schemes_list is undefined and this.props.loadSchemes is undefined
please help am using react-redux
Obviousely in component from where you call Scheme, you import { Schemes }, an unconnected component, instead of Schemes - default connected component. Please check it.
I'm lately struggling with complex HOC and how I can pass through only the new props defined in it and not any other.
More precisely, suppose my HOC makes use of other HOCs which extends its properties, for instance
const withSession = (WrappedComponent) => {
class SessionProvider extends React.PureComponent {
constructor(props) {
super(props);
this.login = this.login.bind(this);
}
login() {
console.log('login'); // this will dispatch some action
// this.props.dispatch...
}
render() {
return (
<WrappedComponent
doLogin={this.login}
{...this.props}
/>
);
}
}
const mapStateToProps = null;
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
return compose(
withConnect,
withRouter,
)(injectIntl(SessionProvider));
};
Here the SessionProvider makes use of dispatch and injectIntl which attach properties to its props. However, I don't want to pass those props down to the wrapped component. The idea is to have a SessionProvider HOC which has some API call but only extends the wrapped component with login.
I noticed that if keep {...this.props}, the wrapped component will also get all the props used by the HOC which I don't want to pass through.
So I thought to explicitly define which properties to pass through by decomposing this.props by changing the HOC render method:
render() {
const { dispatch, intl, ...otherProps } = this.props;
return <WrappedComponent doLogin={this.login} { ...otherProps} />;
}
However what happens with this is that if the WrappedComponent itself has dispach or intl props, those are not passed-through the HOC.
Is there anything wrong in what I'm doing? Any better approach? Am I missing anything?
There's nothing wrong in what you're doing. Prop name conflicts is a known issue when using HOCs. So, as far as I can tell, the best alternative you could use is Render Props pattern, which helps to keep components render as declarative as possible. For your case, consider something like this:
class Session extends React.PureComponent {
constructor(props) {
super(props);
this.login = this.login.bind(this);
}
login() {
console.log("login"); // this will dispatch some action
// this.props.dispatch...
}
// ...
render() {
return (
<React.Fragment>
{this.props.children({
doLogin: this.login
doLogout: this.logout
// ...
})}
</React.Fragment>
);
}
}
// ...
return compose(
withConnect,
withRouter
)(injectIntl(Session));
And use it from another components:
// ...
render() {
return (
<Session>
{({ doLogin, doLogout }) => (
<React.Fragment>
<SomeComponent doLogin={doLogin} />
<button onClick={doLogout}>Logout</button>
</React.Fragment>
)}
</Session>
)
}
UPDATE:
There's a pretty promising Hooks Proposal available in v16.7.0-alpha. I'm not quite familiar with them yet, but they tend to solve components reusability more efficiently.
You need to copy static properties, for that i use below code.. you can add more properties as per your need
export const REACT_STATICS = {
childContextTypes: true,
contextTypes: true,
defaultProps: true,
displayName: true,
getDefaultProps: true,
mixins: true,
propTypes: true,
type: true
};
export const KNOWN_STATICS = {
name: true,
length: true,
prototype: true,
caller: true,
arguments: true,
arity: true
};
export function hoistStatics(targetComponent, sourceComponent) {
var keys = Object.getOwnPropertyNames(sourceComponent);
for (var i = 0; i < keys.length; ++i) {
const key = keys[i];
if (!REACT_STATICS[key] && !KNOWN_STATICS[key]) {
try {
targetComponent[key] = sourceComponent[key];
} catch (error) {}
}
}
return targetComponent;
}
// in HOC
const hoistedSessionProvider = hoistStatics(SessionProvider, WrappedComponent);
// use hoistedSessionProvider in compose
const MainNavigator = StackNavigator({
Home: {
screen: Tabs
},
Collection_Products : {
screen : Collection_Products,
navigationOptions : ({ navigation }) => ({
title : `${navigation.state.params.name.toUpperCase()}`
}),
},
MainProduct : {
screen : (props) => <MainProduct {...props} />,
},
});
export default class MainNav extends React.Component {
constructor() {
super();
this.state = {
checkout: { lineItems: { edges: [] } }
};
}
method1 = (args) => {
}
render() {
return (
<View style={{flex: 1}}>
<MainNavigator checkout= {this.state.checkout} method1 = {this.method1}/>
</View>
);
}
}
I am using react native to build application. I want to pass method 1 to different child components. How can I pass method1 function in MainProduct Component? With the syntax I am using I am only able to pass navigator props to child components.
The accepted answer is outdated for React Navigation v5 and v6
screenProps is no longer available, which caused several problems.
Use react context instead
Quote from react-navigation 5.x,
Due to the component based API of React Navigation 5.x, we have a much better alternative to screenProps which doesn't have these disadvantages: React Context. Using React Context, it's possible to pass data to any child component in a performant and type-safe way, and we don't need to learn a new API!
Alternatively, you may use routeProps
Passing parameters to routes. v6 doc
navigation.navigate('RouteName', { /* params go here */ })
In case you are new to context
Here is how you may use context to pass props.
Example:
import React from 'react'
// Create context outside of your component
// which can be export to your child component
const MyContext = React.createContext()
export default function MyParentComponent() {
const myContext = {
whatIWhatMyChildToKnow: 'Hi, I am your father.',
}
return (
<MyContext.Provider value={myContext}>
// Dont pass your own props to your navigator
// other than RNavigation props
<YourStackNavigator>
...stack logic
</YourStackNavigator>
</MyContext.Provider>
)
}
// access your context value here
function MyChildScreen() {
const { whatIWhatMyChildToKnow } = React.useContext(MyContext)
// will display 'Hi, I am your father.'
return <span>{whatIWantMyChildToKnow}</span>
}
You need to send the props as below. You need to send props name as 'screenProps' literally then only it is send. Yeah, This is little strange. I tried to change the name of the props, it did not get trough.
const propsForAll = {
checkout:/*your checkout data */,
method1: /**Your medhod1*/
}
<View style={{flex: 1}}>
<MainNavigator screenProps={propsForAll}/>
</View>
I think you may want screenProps, from the docs:
const SomeStack = StackNavigator({
// config
});
<SomeStack
screenProps={/* this prop will get passed to the screen components as
this.props.screenProps */}
/>
I built a react component that imports a Json file into an array to map the result. I need that array in another component. I don't know if I must built this component inside the new component or if there's a method to export the needed array (data). The array source is updated every 4 seconds.
Thanks for your help.
My first component is:
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class Ramas extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
const fetchData = () => {
axios
.get('http://localhost:8888/dp_8/fuente/procesos_arbol.json')
.then(({ data })=> {
this.setState({
data: data
});
console.log(data);
})
.catch(()=> {console.log('no recibido');});
};
fetchData();
this.update = setInterval(fetchData, 4000);
} // final componentDidMount
render() {
const initialData = this.state.data.map((el) => {
return (
<p>id={ el.id } | name - { el.name } | padre - {el.parent}</p>
);
});
return (<div className="datos_iniciales">
{ initialData }
</div>);
}
}
ReactDOM.render(
<Ramas />,
document.getElementById('container')
);
make one top level component that can contain the two components.
in the Ramas component ->
const updatedData = setInterval(fetchData, 4000);
this.props.datasource(updatedData);
write a new top level component ->
class TopComponent Extends React.Component{
state = {data: ''}
handleDataUpdate = (updatedData) => {
this.setState({data: updatedData});
}
render = () => {
<Ramas datasource={this.handleDataUpdate}>
<SecondComponent updatedData={this.state.data}>
</Ramas>
}
}
now from SecondComponent updatedData prop you can get the fresh data
By the way it is in ES7 syntax I wrote
If you have parent component, you should pass function from it to this component as a prop.
That function will than set state and data will flow one way as it's imagined with ReactJS.
For example instead of this.setState, you could call
this.props.jsonToArray
and in jsonToArray you should call setState which will pass data to that seccond component.
react-apollo provides the ability to convert component props to query variables:
<MyComponentWithData propsForQueryVariables={...} />
But I need start query with variables from wrapped component.
Something like:
class MyComponent extends React.Component {
//...
onReady() {
// should be first request to server
this.props.refetch({
// variables here
})
}
onFilterChanged() {
this.props.refetch({
// new variables here
})
}
}
const MyComponentWithData = graphql(QUERY, {
options: {
waitUntilComponentStartQuery: true
// pollInterval:...
},
props({ data: { items, refetch } }) {
return {
items: items,
refetch: refetch
};
}
})(MyComponent);
UPDATE
QUERY for MyComponent looks like
query getItems($filter: JSON!) {
items(filter: $filter) {
id
name
}
}
filter is not nullable. So the first request should have valid variable filter, and this variable should be created in wrapped component.
You can pass the parent props to the variables of the initial fetch in the graphql HoC, like this:
ParentComponent.jsx
import ChildComponent from './ChildComponent';
const ParentComponent = () => <ChildComponent filterPropValue="myDefaultFilterValue" />;
export default ParentComponent;
ChildComponent.jsx
class ChildComponent extends React.Component {
refresh() {
this.props.refetch({
filter: 'mynewFilterValue'
});
}
render() {
return (
<div>I am a child component with {this.props.items.length} items.</div>
);
}
}
export default graphql(MyQuery, {
options: (props) => ({
variables: {
filter: props.filterPropValue
}
}),
props: ({ data: { items, error, refetch }) => ({
items,
error,
refetch
})
})(ChildComponent);
Any subsequent refetches with new parameters can then be dealt with via refetch().
refetch accepts a variables object as argument, see the documentation.