Does onClick() pass as a props by default in React Form? - javascript

I have two component. CourseInput.js and Button.js. In the CourseInput.js component I create a React form and inside that form I use Button component. I pass type as a props only. But in the Button.js component how I can access onClick function though I haven't pass it in Button.js component. How Button.js component is getting onClick function as props in onClick= {props.onClick}.
CourseInput.js
import React, { useState } from "react";
import Button from "../../UI/Button/Button";
import "./CourseInput.css";
const CourseInput = (props) => {
console.log(props);
const [enteredValue, setEnteredValue] = useState("");
const goalInputChangeHandler = (event) => {
setEnteredValue(event.target.value);
};
const formSubmitHandler = (event) => {
event.preventDefault();
props.onAddGoal(enteredValue);
};
return (
<form onSubmit={formSubmitHandler}>
<div className="form-control">
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</div>
<Button type="submit">Add Goal</Button>
</form>
);
};
export default CourseInput;
Button.js
import React from "react";
import "./Button.css";
const Button = (props) => {
return (
<button type={props.type} className="button" onClick={props.onClick}>
{props.children}
</button>
);
};
export default Button;
*My code is working as expected but how this props.onClick works? *

Your onClick is undefined for the button and since its JS, it won't complain, even if you remove it, the code will work. When you type the button as submit, it will just invoke the onSubmit function on the form.

Related

Can we access functions outside of stateless component in react?

Trying to invoke modal function from outside, but not possible. Achieved with class component using static method and properties, but this will not work there. Observers are giving some issues, finally landing to stateless component. How best way we can make this work?
//Inside
import Modal from './modal';
// Not working
<Button onClick={Modal.showModal}
<Modal />
//Outside
export const Modal = () => {
const [visible, setVisible] = useState(false);
const showModal = () => {
setVisible(true);
}
return(
<Dialog visible={visible}>Hello Test</h1>
)
}
You can declare setVisible in parent component:
const [visible, setVisible] = useState(true);
Then pass the set function to Modal:
<Modal visible={visible} setVisible={setVisible} />
Set visible from outside:
<button onClick={() => setVisible(!visible)}>Click</button>
Sample: https://codesandbox.io/s/competent-bassi-x3dqd?file=/src/App.js:267-325
You will need to "lift the state up" to a component that will pass the state and the state handlers to his children, or use some global state with redux or context.
Example:
<App>:
import Modal from "./modal";
import { useState } from "react";
const [visible, setVisible] = useState(false);
const showModal = () => {
setVisible(true);
};
return (
<>
<Button onClick={showModal}> Hide Modal </Button>
<Modal visible={visible} />
</>
);
<Modal>:
export default const Modal = ({ visible }) => {
return (
<Dialog visible={visible}>
<h1>Hello Test</h1>
</Dialog>
);
};
Also, by only using the export keyword, you can import only single export from a module, therefore you can't use the default import syntax import Modal from "..."
and you will be required to import as such - import { Modal } from '...'
I managed to solve usingEffect here, but not sure if this is perfect for my implementation. I am trying to update the state for already mounted component.
useEffect(() => {
if (props.visible) {
showModal();
}
}, [props.visible]);
And handled resetState with a callback prop

How to implement API search onClick

My project is using React, Axios, and movieDB's API. I am trying to make it so that when I type out a movie title and either hit enter and/or click submit then the API will return the title of the movie searched for as an h1 element.
currently, the code works as soon as I refresh the browser and the only way for it to function properly is if I go into the code and replace ${query} with what I want to search for, ie joker. I have tried adding the onclick to the button : <button onclick="componentDidMount()">submit</button>. This did not do anything, however.
App.js
import React from "react"
import Movielist from './components/Movielist'
function App() {
return (
<div>
<input type="search" />
<button onclick="componentDidMount()">submit</button>
<h1 id="title">title</h1>
<Movielist />
</div>
)
}
export default App
Movielist.js
import React from 'react';
import axios from 'axios';
export default class Movielist extends React.Component {
state = {
title: ""
}
componentDidMount() {
const API_KEY = '*****************';
const query = document.getElementById('search');
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`)
.then(res => {
const title = res.data['results'][0]['title'];
this.setState({ title });
})
}
render() {
return (
<h1>{this.state.title}</h1>
)
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
You have a few things wrong here:
The structure of your app is not great, eg. seperate out your API calls
You are calling a lifecycle method, these are called automatically
Don't use DOM selectors like getElementById in React
Use camelcase event listeners (onclick should be onClick)
Use callbacks with event listeners or they will fire immediatly
You included your API key on stackoverflow, big mistake!
Try this:
https://codepen.io/alexplummer/pen/YzwyJOW
import React, {useState} from "react";
const MovielistSearch = props => (
<>
<input type="search" onChange={e => props.saveSearchInput(e.target.value)} />
<button type="button" onClick={() => props.handleSearch()}>
Search
</button>
</>
);
const getMovies = props => {
return ['Title 1', 'Title 2', 'Title 3'];
// ADD REAL API HERE
// const API_KEY = '';
// const getMovies = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${props.searchTerm}`);
// return getMovies.data;
}
const MovieList = props => (
<ul>
{props.foundMovies.map(thisMovie=><li>{thisMovie}</li>)}
</ul>
);
const App = () => {
const [searchInput, setSearchInput] = useState("");
const [foundMovies, setFoundMovies] = useState([]);
const movieSearch = ()=> {
if (searchInput == null) return;
const foundMovies = getMovies(searchInput);
setFoundMovies(foundMovies);
}
return (
<div>
<h1 id="title">Movie list</h1>
<MovielistSearch saveSearchInput={setSearchInput} handleSearch={movieSearch} />
<MovieList foundMovies={foundMovies} />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
The problem might be here:
const query = document.getElementById('search');
It returns an HTML element. Try document.getElementById('search').innerText

React HOC with invoked functions; "Functions are not valid as a React child"

Please see this codesandbox. I am trying to create a react HOC that receives both a functional component and a function that gets invoked in the HOC.
To do this, I need to create a react component that returns this HOC. The function that needs to be invoked in the HOC needs to share it's scope with the react component, since it might do things like change the component's state or make api calls. My attempt at this is is trying to create a nested react component, but when I do, I get the following error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it in SampleComponent
How can I create a react HOC that accepts both a react component and a function to be invoked? Thanks! The code can also be found below:
index.tsx:
import * as React from "react";
import { render } from "react-dom";
import SampleComponent from "./sampleComponent";
render(<SampleComponent />, document.getElementById("root"));
sampleWrapper.tsx:
import * as React from "react";
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
const Component: React.FC = () => (
<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>
);
return Component;
};
export default sampleWrapper;
sampleComponent.tsx:
import * as React from "react";
import sampleWrapper from "./sampleWrapper";
const SampleComponent: React.FC = () => {
const [title, setTitle] = React.useState("hello world");
const handleTitleChange = (): void => {
setTitle("This is a new title");
};
const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;
return sampleWrapper(SampleInnerComponent, handleTitleChange);
};
export default SampleComponent;
As your sampleWrapper returns a functional component. What you need to do is save the returned functional component into a variable and render the component the same way you do functional component. i.e
import * as React from "react";
import sampleWrapper from "./sampleWrapper";
const SampleComponent: React.FC = () => {
const [title, setTitle] = React.useState("hello world");
const handleTitleChange = (): void => {
setTitle("This is a new title");
};
const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;
const ReturnedSampleComponent = sampleWrapper(SampleInnerComponent, handleTitleChange);
return <ReturnedSampleComponent />;
};
export default SampleComponent;
You can check this codesandbox
It seems you are not returning the React Element, but the Component. You want to return the element React.ReactElemnt. This is what you want i think:
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
return (<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>);
};
another alternative :
import * as React from "react";
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
const Component: React.FC = () => (
<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>
);
return <Component/>;
};
export default sampleWrapper;

Can I mapDispatchToProps without mapStateToProps in Redux?

I am breaking apart Redux' todo example to try to understand it. I read that mapDispatchToProps allows you to map dispatch actions as props, so I thought of rewriting addTodo.js to use mapDispatchToProps instead of calling dispatch(addTodo()). I called it addingTodo(). Something like this:
import React from 'react';
import {connect} from 'react-redux';
import addTodo from '../actions';
let AddTodo = ({addingTodo}) => {
let input;
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
addingTodo(input.value)
input.value = ""
}}>
<input ref={node => {
input = node
}} />
<button type="submit">Submit</button>
</form>
</div>
)
}
const mapDispatchToProps = {
addingTodo: addTodo
}
AddTodo = connect(
mapDispatchToProps
)(AddTodo)
export default AddTodo
However, when I run the app, I get this error: Error: Invalid value of type object for mapStateToProps argument when connecting component AddTodo.. I never used mapStateToProps to begin with on AddTodo component, so I was not sure what was wrong. My gut feeling says that connect() expects mapStateToProps to precede mapDispatchToProps.
The working original looks like this:
import React from 'react';
import {connect} from 'react-redux';
import addTodo from '../actions';
let AddTodo = ({dispatch}) => {
let input;
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ""
}}>
<input ref={node => {
input = node
}} />
<button type="submit">Submit</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
Complete repo can be found here.
So my question is, is it possible to do mapDispatchToProps without mapStateToProps? Is what I am trying to do an acceptable practice - if not, why not?
Yes, you can. Just pass null as first argument:
AddTodo = connect(
null,
mapDispatchToProps
)(AddTodo)
Yes, it's not just acceptable practice, it's recommended way to trigger actions. Using mapDispatchToProps allows to hide the fact of using redux inside your react components

How to get event from components to container in React Redux

I'm new to Redux.
I handled the basic Facebook Flux architecture very easily and made some nice app with it.
But I struggle very hard to get very simple Redux App to work.
My main concern is about containers and the way they catch events from components.
I have this very simple App :
CONTAINER
import { connect } from 'react-redux'
import {changevalue} from 'actions'
import App from 'components/App'
const mapStateToProps = (state) => {
return {
selector:state.value
}
}
const mapDispatchToProps = (dispatch) => {
return {
onClick: (e) => {
console.log(e)
dispatch(changeValue())
}
}
}
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App)
export default AppContainer;
Component
import React, {Component} from 'react'
import Selector from 'components/Selector'
import Displayer from 'components/Displayer'
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/>
<Displayer />
</div>
)
export default App;
CHILD COMPONENT
import React, {Component} from 'react'
const Selector = ({onClick}) => (
<div onClick={onClick}>click me</div>
)
export default Selector;
onClick event does not reach the container's mapDispatchToProps.
I feel that if I get this work, I get a revelation, and finally get the Redux thing! ;)
Can anybody help me get this, please ? (The Redux doc is TOTALLY NOT helpfull...)
The problem is in the App component. In the onClick property of the Selector component, you're passing a function which returns the definition of a function, not the result.
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/> // here is the problem
<Displayer />
</div>
)
You should simply do this instead:
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick(e)}/>
<Displayer />
</div>
)
Or even simpler:
const App = (selector, onClick) => (
<div>
<Selector onClick={onClick}/>
<Displayer />
</div>
)

Categories

Resources