I looked at many other answers but I couldn't figure it out. Here is my code:
// userInputActions.js
...
export function dummy() {
console.log('dummy function called');
}
...
// *userInputPage.js*
import * as userInputActions from '../actions/userInputActions';
class UserInput extends React.Component {
constructor(props) {
super(props);
this.state = {
};
// When un-commented it shows '*f dummy()*' is imported
// console.log('actions: ', userInputActions);
this.dummy = this.dummy.bind(this);
}
render () {
return (
<div className="container-fluid align-items-center">
<FieldLevelValidationForm onSubmit={this.dummy}/>
</div>
);
}
}
const mapStateToProps = (state) => ({
});
const mapDispatchToProps = (dispatch) =>
bindActionCreators(
{
...userInputActions
}, dispatch
);
export default connect(mapStateToProps, mapDispatchToProps)(UserInput);
Note 'FieldLevelValidationForm' is a redux-form and onSubmit is one of the form function arguments.
I tried various things and the bind function does not work. Can someone please let me know where am I going wrong. I think it has something to do with the render() function and the lifetime of the component but I do not know enough yet.
Edit
Thank you - learned a lot from all answers. All of them work. I wish I could give more than one check-mark. However, I think the most appropriate use for my case is to call it as a prop and dispatch an action as so on.
The dummy function is passed as props, so you should access it with this.props.dummy in your render() instead.
There's also no need to bind it to this as it's not using the this instance.
The dummy function you are trying to bind is not in this class. So it would just be:
this.dummy = userInputActions.dummy.bind(this);
Function definition of dummy is missing.
constructor(props) {
super(props);
this.state = { };
this.dummy = this.dummy.bind(this);
}
dummy(e) {
// form submission action
userInputActions.dummy(); // maybe
}
render () {
return (
<div className="container-fluid align-items-center">
<FieldLevelValidationForm onSubmit={this.dummy}/>
</div>
);
}
Related
Hi guys I can't see my Error here hope someone can hlep...
This is my fetch Data class:
export default class Auftrag extends Component {
state = {
auftraege: "Test",
};
getAuftraege = () => {
axios.get("Auftraege/auftraege").then(e => {
this.setState({
auftraege: e.data,
});
console.log(e.data);
});
};
componentDidMount() {
this.getAuftraege();
}
render() {
return (
<>
<AuftragDisplay test={this.state.auftraege} ></AuftragDisplay>
</>
);
}
}
And this is my constructor in my Display class:
constructor(props) {
super(props);
console.log(props);
}
The axios Request is getting fired and I get the right data in my console. But It is not getting passed to my Component.
Hope someone knows whats wrong and can help me
SOLVED:
Thx to san I tried it and could solve the problem. I got the data passed but console.log() was called before the update so I got the old data. THX again
Your code looks fine. you can see below same code with different api as an example
class Auftrag extends Component {
state = {
auftraege: "Test",
};
getAuftraege = () => {
axios
.get("https://jsonplaceholder.typicode.com/posts/1")
.then(e => this.setState({auftraege: e.data}))
};
componentDidMount() {
this.getAuftraege();
}
render() {
return (
<>
<AuftragDisplay test={this.state.auftraege} ></AuftragDisplay>
</>
);
}
}
const AuftragDisplay = ({test}) =><h2>Hi--->{test.title}</h2>
Just put the state inside constructor of Auftrag class, I should work.
I can not get my component to re-render when an internal variable changes. The system I working on uses functional components. The case is like this:
export const myComponent = (props: compPropsType) => {
const myClassNames ....
let objectList= getObjectList(window.location.hash, props.pageTree);
window.addEventListener('hashchange', () => {
console.log('hello');
objectList = getObjectList(window.location.hash, props.pageTree);
});
return (
<>
<header className={headerClassNames}>
<Block className={...}>
...
<myChildComp objList={objectList}>
...
)
};
The problem is to render <myCildComp> when the hash updates. (objectListis an array of strings, used to create navigation toolbar.)
When I click a link on the page, hello is written to console, so the listener is working, but it does not re-render the child component.
a function component in react is equivalent to the render() function in a class component.
so in your case you're adding an eventListener every time the component is re-rendered which creates a memory leak, you should use useEffect hook instead to add it once and remove it when the component/hook is destroyed.
your component doesn't re-render because nothing is telling it to.. components in react only re-render when either state or props changes.. so in your case I think you'd need to add objectList to state combined with useEffect hook like so
export const myComponent = (props: compPropsType) => {
const myClassNames ....
const [objectList, setObjectList] = useState([])
const onHashChanged = () => {
console.log('hello')
let newObjectList = getObjectList(window.location.hash, props.pageTree)
setObjectList(newObjectList)
}
useEffect(() => {
window.addEventListener('hashchange', onHashChanged)
return () => window.removeEventListener('hashchange', onHashChanged)
}, [])
return (
<>
<header className={headerClassNames}>
<Block className={...}>
...
<myChildComp objList={objectList}>
...
)
};
when using an empty array as a second argument in useEffect it will only be called once equivalent to componentDidMount and I'm returning a callback function to unsubscribe/remove the listener which works similar to componentWillUnmount
As suggested in a comment, I rewrote the function to be a class:
export class myComponent extends React.PureComponent<PropsTypes> {
constructor(props) {
super(props)
this.state = {objList = getObjectList(window.location.hash, this.props.pageTree)};
}
componentDidMount(): void {
window.addEventListener('hashchange', () => {
this.setState({
breadcrumbList: getObjList(window.location.hash, this.props.pageTree),
});
});
}
render() {
return (....
<myChildComp objList={this.state.objList}>
...
This works, but it is the best way?
First of all, I'm really new into React, so forgive my lack of knowledge about the subject.
As far as I know, when you setState a new value, it renders again the view (or parts of it that needs re-render).
I've got something like this, and I would like to know if it's a good practice or not, how could I solve this kind of issues to improve, etc.
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = {
key: value
}
this.functionRender = this.functionRender.bind(this)
this.changeValue = this.changeValue.bind(this)
}
functionRender = () => {
if(someParams !== null) {
return <AnotherComponent param={this.state.key} />
}
else {
return "<span>Loading</span>"
}
}
changeValue = (newValue) => {
this.setState({
key: newValue
})
}
render() {
return (<div>... {this.functionRender()} ... <span onClick={() => this.changeValue(otherValue)}>Click me</span></div>)
}
}
Another component
class AnotherComponent extends Component {
constructor (props) {
super(props)
}
render () {
return (
if (this.props.param === someOptions) {
return <div>Options 1</div>
} else {
return <div>Options 2</div>
}
)
}
}
The intention of the code is that when I click on the span it will change the key of the state, and then the component <AnotherComponent /> should change because of its parameter.
I assured that when I make the setState, on the callback I throw a console log with the new value, and it's setted correctly, but the AnotherComponent doesn't updates, because depending on the param given it shows one thing or another.
Maybe I need to use some lifecycle of the MyComponent?
Edit
I found that the param that AnotherComponent is receiving it does not changes, it's always the same one.
I would suggest that you'll first test it in the parent using a simple console.log on your changeValue function:
changeValue = (newValue) => {
console.log('newValue before', newValue);
this.setState({
key: newValue
}, ()=> console.log('newValue after', this.state.key))
}
setState can accept a callback that will be invoked after the state actually changed (remember that setState is async).
Since we can't see the entire component it's hard to understand what actually goes on there.
I suspect that the newValue parameter is always the same but i can't be sure.
It seems like you're missing the props in AnotherComponent's constructor. it should be:
constructor (props) {
super(props) // here
}
Try replacing the if statement with:
{this.props.param === someOptions? <div>Options 1</div>: <div>Options 2</div>}
also add this function to see if the new props actually get to the component:
componentWillReceiveProps(newProps){
console.log(newProps);
}
and check for the type of param and someOptions since you're (rightfully) using the === comparison.
First, fat arrow ( => ) autobind methods so you do not need to bind it in the constructor, second re-renders occur if you change the key of the component.
Ref: https://reactjs.org/docs/lists-and-keys.html#keys
Let us assume we have a statefull React component (configured to work with Redux):
export class SomeComponent extends Component {
state = {
someObject: {}
};
componentWillMount() {
this.props.getNews();
this.props.getFakeNews();
}
render() {
const {
news,
fakeNews
} = this.props;
if(_.isEmpty(news) || _.isEmpty(fakeNews)){
return <div>Loading</div>
}else{
return <div>Here all component stuff</div>
}
}
SomeComponent.propTypes = {
news: PropTypes.array.isRequired,
fakeNews: PropTypes.array.isRequired
};
export const Some = connect(
state => ({
news: newsSelectors.list(state),
fakeNews: fakeNewsSelectors.list(state)
}),
{
getNews,
getFakeNEws
}
)(withStyles(styles)(SomeComponent), withRouter(SomeComponent));
This component will re-render two times during getting news and fake news. In the render method we need to check if both of them are loaded.
Is there any way to trigger render only when all props are loaded?
In a perfect scenario I'd like to have no detailed null/empty check on the set of props. I believe React or Redux should perform this operation on its own as long the prop is configured as required.
You can add a lifecycle method `shouldComponentUpdate(nextProps, nextState).
You can add the following method and it should resolve it for you:
shouldComponentUpdate(nextProps, nextState) {
if (_.isEmpty(nextProps.news) || _.isEmpty(nextProps.fakeNews)) {
return false;
}
return true;
}
You could do something like:
// HOC factory
function ifComponent (predicate, PlaceHolder) {
return Component => class If extends React.Component {
render () {
if (predicate(this.props)) {
return <Component {...this.props} />
}
return <PlaceHolder {...this.props} />
}
}
}
}
// create the customHOC
const whenPropsLoaded = ifComponent(props => props.news && props.fakeNews, Loader);
// compose the two HOCs using the `compose` function in redux (simple function composition)
const News = compose(
connect(getNewsProps),
whenPropsLoaded(DisplayNews)
);
As a side note you may be interested in the recompose utility library bad its branch HOC (docs here). I think this is pretty much what you want as you seem to know about HOCs.
If you want to avoid null and undefined values from redux. You can use Selectors it was very easy to avoid those things.
const newsSelectors = (state) => {
if(!state.list) { *//list is null or undefined*
return [] or {} *//your wish (Proptypes required value)*
}
else {
return state.list
}
}
export { newsSelectors };
I think you can solve the issue if you rewrite the render function as below.
render() {
const {
news,
fakeNews
} = this.props;
return (
{news && fakeNews ?
<div>Here all component stuff</div>
: <div>Loading</div> }
)
}
I hope this helps you.
As the title suggests, only after the first message received in my chat-window - this initial message is retrieved from a GET request so it's not synchronous - I want to show/render a button. At the moment it throws an error saying I cant set the state within the render method.
I also tried the show logic in the button class as well as the 'parent' class which is my messagelist which I'm putting the button in its render method.
There is this.props.messages which is an array of the messages and so is 'messages'. this.props.messages[0].data.text is the first message, although it does console many times each messsage in the dev tools when i try console it, and of course it throws the setState error when i try to show the button.
I have a simple button class:
class Button extends Component {
render() {
return (
<div>
{<button>Return</button >}
</div>
)
}
}
export default Button;
and my messageList class, where I have the this.props.messages which is an array of the messages, this.props.messages[0] is the first message , and message..which console's every single message if i console.log it.
If i write either (if message.data.text OR this.props.messages[0] === 'my first string') { console.log ('..... '}then it always counts as true and consoles and the setstate goes into a loop.
import Message from './Messages'
import Button from './Button'
class MessageList extends Component {
constructor(props) {
super(props);
this.state = {
showing: false,
};
this.showButton = this.showButton.bind(this);
}
showButton() {
const { showing } = this.state;
this.setState({
// toggle value of `showing`
showing: !showing,
});
}
componentDidUpdate(prevProps, prevState) {
this.scrollList.scrollTop = this.scrollList.scrollHeight;
}
onlyInitialMessage(message) {
if (this.props.messages[0].data.text = `Hi I'm Joe your store assistant, I'm here to help. Here's what I can do: Answer questions on store policies, process a return or just general inquiries.`) {
this.showButton();
}
}
// way to render a function.
// {this.renderIcon()}
render() {
return (
<div className="sc-message-list" ref={el => this.scrollList = el}>
{this.props.messages.map((message, i) => {
{ this.onlyInitialMessage() }
return <Message message={message} key={i} />
})}
{this.state.showing && <Button />}
</div>)
}
}
I'm not sure If I have my logic in the wrong place here? I tried to move it around lots of times, I am new to React!
Firstly, The issue is that you are setting state in the render method indirectly by calling { this.onlyInitialMessage() } in render.
Secondly, your if condition is not comparing value but assinging value which will always return true
if (this.props.messages[0].data.text === `Hi I'm Joe your store assistant, I'm here to help. Here's what I can do: Answer questions on store policies, process a return or just general inquiries.`) {
To solve it, you must call onlyInitialMessage within componentDidMount
import Message from './Messages'
import Button from './Button'
class MessageList extends Component {
constructor(props) {
super(props);
this.state = {
showing: false,
};
this.showButton = this.showButton.bind(this);
}
componentDidMount() {
this.onlyInitialMessage();
}
showButton() {
const { showing } = this.state;
this.setState({
// toggle value of `showing`
showing: !showing,
});
}
componentDidUpdate(prevProps, prevState) {
this.scrollList.scrollTop = this.scrollList.scrollHeight;
}
onlyInitialMessage(message) {
if (this.props.messages[0].data.text == `Hi I'm Joe your store assistant, I'm here to help. Here's what I can do: Answer questions on store policies, process a return or just general inquiries.`) {
this.showButton();
}
}
// way to render a function.
// {this.renderIcon()}
render() {
return (
<div className="sc-message-list" ref={el => this.scrollList = el}>
{this.props.messages.map((message, i) => {
return <Message message={message} key={i} />
})}
{this.state.showing && <Button />}
</div>)
}
}