I'm passing storyLike function from parent to child.Sometimes when onPress is clicked I get following error: this.props.storyLike(props.story) is not a function.
I thought it's a context issue but couldn't resolve it.
Here is my Parent:
renderRow(item) {
return (
<Story
story={item}
openStory={this.props.openStory}
getProfile={this.props.getProfile}
storyLike={this.props.storyLike}
/>
);
}
Here is child component Story, just passing props to StoryFooter component:
class Story extends Component {
render() {
return (
<View style={styles.container}>
<StoryHeader {...this.props} />
<StoryContent {...this.props} />
<StoryFooter {...this.props} storyViewType={"feed"} />
</View>
);
}
}
Bellow is child component, I'm triggering storyLike from onPress changeLikedStatus function:
class StoryFooter extends Component {
state = { isLiked: false, likes: 0 };
changeLikedState(props) {
const numberOflikes = this.state.isLiked
? this.state.likes - 1
: this.state.likes + 1;
if (this.state.isLiked) {
this.setState({ isLiked: false, likes: numberOflikes });
} else {
this.setState({ isLiked: true, likes: numberOflikes });
}
this.props.storyLike(props.story);
}
<TouchableOpacity onPress={() => this.changeLikedState(this.props)}>
This is how your parent should look like
renderRow(item) {
return (
<Story
// ... other props
storyLike={(prop) => this.props.storyLike(prop)}
/>
);
}
This way you explicitly tell that the child will be passing a prop to the function.
Hopefully your storyLike function is an arrow function that looks like this:
storyLike = (prop) => {
// code
}
Related
I have an app that already uses the context api, it has a number of providers. I have added a new provider called VideoContext.
All I want is to read its activeVideo value (set elsewhere in the app)
class App extends Component {
state = {
videoState: 0,
};
setVideoState(){
//logic to alter the videoState.
}
...
render() {
return (
// Config Context
<ConfigContext.Provider value={this.decorateConfigContextFromProps()}>
<VideoContext.Provider value={this.state.videoState}>
{/* Theme Context */}
<SomeOtherProvider theme={this.decorateStyledContextFromProps()}>
{/* Redux Store Context */}
<Provider store={this.rootStore}>
<View style={{ flex: 1 }}>
<StatusBar />
<ConnectedApp />
</View>
</Provider>
</SomeOtherProvider>
</VideoContext.Provider>
</ConfigContext.Provider>
);
}
}
App.contextType = ConfigContext;
child component - miles down the tree
class DeepChild extends Component {
state = {
activeVideo: '',
};
shouldComponentUpdate(nextProps: Props, nextState) {
if (this.state.activeVideo !== nextState.activeVideo) {
return true;
}
return //otherwise do some other logic here
}
...
render() {
return (
<View/>
<VideoContext.Consumer>
/* yuck! */
{({activeVideo}) => {
if(activeVideo !== this.state.activeVideo){
this.setState({activeVideo: activeVideo}, () => {
console.log('this did indeed update the state');
})
}
}}
</VideoContext.Consumer>
<FlatList...containing many items.../>
</View>
);
}
}
How can I read the value provided by the VideoContext Provider inside the Child class component, without having to bury it inside the render function? I haven't seen any docs on this.
Thanks.
Use contextType like this
static contextType = VideoContext;
then value will be accessible like this.context.activeVideo
e.g.
class DeepChild extends Component {
static contextType = VideoContext;
state = {
activeVideo: '',
};
shouldComponentUpdate(nextProps: Props, nextState) {
if (this.state.activeVideo !== nextState.activeVideo) {
return true;
}
return //otherwise do some other logic here
}
...
render() {
return (
<View/>
<VideoContext.Consumer>
/* yuck! */
{({activeVideo}) => {
if(activeVideo !== this.state.activeVideo){
this.setState({activeVideo: activeVideo}, () => {
console.log('this did indeed update the state');
})
}
}}
</VideoContext.Consumer>
<FlatList...containing many items.../>
</View>
);
}
}
I have a function called addFunc in my main Class. This class calls the RenderItem function to display a list of items. Each item has an onClick that should execute the addFunc function.
I am unable to call the addFunc function from within my RenderItem function because they are in different components. How do I get past this?
This is a summary of my code:
const selectedData = []
class Search extends Component {
constructor(props) {
super(props);
this.addFunc = this.addFunc.bind(this);
}
addFunc(resultdata){
console.log(resultdata)
selectedData = [...selectedData, resultdata]
console.log(selectedData)
};
render() {
return (
<ReactiveList
componentId="results"
dataField="_score"
pagination={true}
react={{
and: ["system", "grouping", "unit", "search"]
}}
size={10}
noResults="No results were found..."
renderItem={RenderItem}
/>
);
const RenderItem = (res, addFunc) => {
let { unit, title, system, score, proposed, id } = {
title: "maker_tag_name",
proposed: "proposed_standard_format",
unit: "units",
system: "system",
score: "_score",
id: "_id"
};
const resultdata = {id, title, system, unit, score, proposed}
return (
<Button
shape="circle"
icon={<CheckOutlined />}
style={{ marginRight: "5px" }}
onClick={this.addFunc()}
/>
);
}
You can wrap RenderItem component with another component and then render it,
const Wrapper = cb => {
return (res, triggerClickAnalytics) => (
<RenderItem
res={res}
triggerClickAnalytics={triggerClickAnalytics}
addFunc={cb}
/>
);
};
and renderItem of ReactiveList would be: renderItem={Wrapper(this.addFunc)}
then RenderItem component would be
const RenderItem = ({ res, triggerClickAnalytics, addFunc }) => {
...
see sandbox: https://codesandbox.io/s/autumn-paper-337qz?fontsize=14&hidenavigation=1&theme=dark
You can pass a callback function defined in the parent as a prop to the child component
class Parent extends React.Component {
sayHello(name) => {
console.log("Hello " + name)
}
render() {
return <Child1 parentCallback = {this.sayHello}/>
}
}
and then call it from the child component
class Child1 extends React.Component{
componentDidMount() {
this.props.parentCallback("Foo")
}
render() {
return <span>child component</span>
}
};
I have to call some methods from component B inside component A.
Here is the component B: (In the case that you need more code just request me and I will update my post)
import A from "./A";
export default class B extends Component {
componentDidUpdate (prevProps, prevState) {
this.props.onRef(this);
}
renderContent ( ) {
let toolbarActiveItem = Toolbar.getActiveItem();
if (Toolbar.getActiveItem()=="XXXX") {
this.refs.A._showModalHistory();
} else {
toolbarActiveItem = Toolbar.getActiveItem();
}
return (
<Container>
{(toolbarActiveItem == 'YYY') && <C navigation={this.props.navigation} cb={this.childCallback} info={this.props.navigation.state.params}/> }
</Container>
);
}
render () {
return (
<StyleProvider style={getTheme(Config.theme)}>
<A ref={component => { this.A = component; }} />
<Container ref={this.ref}>
{ this.renderNavbar(this.title) }
{ this.renderToolbar() }
{ this.renderContent() }
{ this.renderDialog() }
</Container>
</StyleProvider>
);
}
Here is the component A:
export default class A extends Component {
constructor(props) {
super();
this.subscription = 0;
this.state = {};
changePage = props.cb;
this.state = {
isModalVisibleHistory: false,
};
}
_showModalHistory = () => this.setState({ isModalVisibleHistory: true });
render() {
return (
<Modal isVisible={this.state.isModalVisibleHistory}>
<View style={styles.BG}>
<ParkHistory></ParkHistory>
</View>
</Modal>
);
}
}
My problem is that, I need to execute this.refs.A._showModalHistory(); in component B but, I see the following error:
React.Children.only expected to receive a single React element child.
I think, I have a problem to deal with rendering multiple components and ref them. I should mention that, when I delete <A ref={component => { this.A = component; }} /> my code works fine but, without calling the method. Could you please help me to solve this issue?
The problem is in the way you are accessing the ref. You have assigned ref to Component A. You are using the callback style to assign ref and so you need not write this.ref.A. Change it to this.A._showModalHistory();
Aslo the way you are setting ref to Container component is incorrect, you need to use callback there
<Container ref={(ref)=> this.ref = ref}>
One other thing is that the StyleProvider component expects a single child element. You should wrap your Container and A component in View
render () {
return (
<StyleProvider style={getTheme(Config.theme)}>
<View>
<A ref={component => { this.A = component; }} />
<Container ref={(ref)=> this.ref = ref}>
{ this.renderNavbar(this.title) }
{ this.renderToolbar() }
{ this.renderContent() }
{ this.renderDialog() }
</Container>
</View>
</StyleProvider>
);
}
I have a react.js component where I want to pass down a bunch of different methods to child components from the parent component, the methods modify the state of the parent component.
class Page extends Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items,
currentItemID: 1
};
this.actions = this.actions.bind(this);
}
render() {
return (
<div className="page">
{
this.state.items.map(item =>
<Item key={item._id} item={item} actions={this.actions} />
)
}
</div>
);
}
actions() {
return {
insertItem: function (itemID) {
const currentItems = this.state.items;
const itemPosition = this.state.items.map((item) => item._id).indexOf(itemID);
const blankItem = {
_id: (new Date().getTime()),
content: ''
};
currentItems.splice(itemPosition + 1, 0, blankItem)
this.setState({
items: currentItems,
lastAddedItemID: blankItem._id
});
},
setCurrentItem: function (itemID) {
this.setState({ currentItemID: itemID });
},
focus: function(itemID) {
return (itemID === this.state.currentItemID);
}
}
}
In my child component, I am trying to use the focus method in the componentDidMount lifecyle method as shown below:
componentDidMount() {
if (this.props.actions().focus(this.props.item._id)) {
this.nameInput.focus();
}
}
However, I am getting the error
Uncaught TypeError: Cannot read property 'currentItemID' of undefined
in the definition of the focus method, within the actions methods. Can anyone point me in the right direction as to why I'm getting the error or an alternative way to pass down multiple actions to child components?
the context is not passed to the function, then the 'this' in the function is that of the function itself and not the component.. you can solve it that way (put the functions in the components):
actions() {
return {
insertItem: this.insertItem.bind(this),
setCurrentItem: this.setCurrentItem.bind(this),
focus: this.focus.bind(this),
}
}
insertItem(itemID) {
const currentItems = this.state.items;
const itemPosition = this.state.items.map((item) => item._id).indexOf(itemID);
const blankItem = {
_id: (new Date().getTime()),
content: ''
};
currentItems.splice(itemPosition + 1, 0, blankItem)
this.setState({
items: currentItems,
lastAddedItemID: blankItem._id
});
},
setCurrentItem(itemID) {
this.setState({ currentItemID: itemID });
},
focus(itemID) {
return (itemID === this.state.currentItemID);
}
but yet, the recomended way is to put the functions in the components like above and remove the actions method and do this:
<Item key={item._id} item={item} actions={{
insertItem: this.insertItem.bind(this),
setCurrentItem: this.setCurrentItem.bind(this),
focus: this.focus.bind(this)
}} />
or
<Item key={item._id} item={item} actions={{
insertItem: () => this.insertItem(),
setCurrentItem: () => this.setCurrentItem(),
focus: () => this.focus()
}} />
I have a NavBarRouteMapper object that I pass to my navbar. However, in the onpress function of one of the buttons I need to access the state, but I'm not sure how to bind 'this' to the object, since it is a non-function. Relevant code as follows
class app extends Component {
state: {
sideMenuIsOpen: boolean,
};
constructor(props: Object) {
super(props);
this.state = {
sideMenuIsOpen: false,
};
};
static NavigationBarRouteMapper = {
LeftButton(route, navigator, index, navState) {
if (index > 0) {
return (
<SimpleButton
// TODO: Make this work, Menu button needs to go to this
// The problem is here. this.state is undefined
onPress={console.log(this.state)}
customText="Back"
style={styles.navBarLeftButton}
textStyle={styles.navBarButtonText}
/>
);
}
},
RightButton(route, navigator, index, navState) {
// TODO change add button for admins
switch (route.title) {
case "Login":
return null;
default:
return (
<SimpleButton
onPress={() => {
navigator.push({
title: "Profile",
component: Profile,
});
}}
customText="Add"
style={styles.navBarRightButton}
textStyle={styles.navBarButtonText}
/>
);
}
},
Title(route, navigator, index, navState) {
return (
<Text style={styles.navBarTitleText}>{route.title}</Text>
);
},
};
render() {
return (
<SideMenu
menu={<Menu navigate={this.navigate} />}
isOpen={this.state.sideMenuIsOpen}
>
<Navigator
ref="rootNavigator"
initialRoute={{
title: "Login",
component: LoginScene,
navigator: this.refs.rootNavigator,
}}
renderScene = {this.renderScene}
navigationBar={
<Navigator.NavigationBar
// Since this is an object, I can't bind 'this' to it,
// and the documentation calls for it to be passed an object
routeMapper={app.NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
</SideMenu>
);
};
}
So you're trying to return the parent's state from the child onClick? If so you can add an onClick which calls the parent onClick that you can pass to the child via props.
var Hello = React.createClass({
getInitialState: function() {
return {test: "testing 1 2 3"};
},
clickMethod: function () {
alert(this.state.test);
},
render: function() {
return <div onClick={this.clickMethod}>Hello {this.props.name}</div>;
}
});
var Child = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
https://jsfiddle.net/reactjs/69z2wepo/
Also if you had a list of components where each component needs to pass a unique piece of data to the parent you can do so using bind.
var Hello = React.createClass({
getInitialState: function() {
return {test: "testing 1 2 3"};
},
clickMethod: function (argument) {
alert(argument);
},
render: function() {
return <div onClick={this.clickMethod.bind(this, "custom argument")}>Hello {this.props.name}</div>;
}
});
var Child = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
https://jsfiddle.net/chrshawkes/64eef3xm/1/