can't call redux action from form submit - reactjs

When I try to submit the search form, it doesn't call the search action.
Is it because I'm passing the search function inefficiently as I'm passing it from one component to another? is there a way I could just use it directly from the import?
If I were to include the SearchBox component inside of Nav itself, it works.
If I added console.log(search) inside of handleSubmit, it outputs the function.
import { search } from './actions/userActions';
class Nav extends React.Component {
render() {
const { search, self } = this.props;
return (
self === null
? <NavLoggedOut search={search} />
: <NavLoggedIn search={search}
)
}
}
const NavLoggedOut = (props) => {
.....
}
const NavLoggedIn = (props) => {
return (
<header>
<SearchBox search={search} />
</header>
)
}
class SearchBox extends React.Component {
constructor() {
super();
this.state = {
name: ''
}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
handleSubmit = event => {
event.preventDefault();
this.props.search(JSON.stringify({name: this.state.name}))
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" id="name" onChange={this.handleChange} placeholder="Search" />
<button type="submit">search</button>
</form>
)
}
}
const mapStateToProps = state => ({
self: state.store.self
});
export default connect(mapStateToProps, {search})(Nav);

Try adding these few lines
import { bindActionCreators } from 'redux';
...
function mapDispatchToProps(dispatch){
return bindActionCreators({
search
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Nav);
Now your search action should be available to the component Nav using this.props.search()
Remember to pass in parameters if it is expecting one (This depends on how you've written your search action)

Add this in your constructor
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
and constructor should look like dis
constructor(props) {
super(props);
this.state = {
name: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

Related

How to update component state that is connected by componentDidUpdate()

I am passing selectedOrderState as props from parent and want to populate the state and that works but can't figure how to change the state for use in an input field with an onChange=(handleChange) function attached to manipulate the data. Seems as though componentDidUpdate() and getDerivedStateFromProps() both seem to lock the state so no change can occur. **componentDidMount also does not work because the selectedOrderState prop comes from an onClick event and so the component had already mounted.
Code below - Any thoughts would be helpful!
import React, { Component } from 'react'
export class addOrder extends Component {
state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
DeliveryDate:"",
};
//Functs
componentDidUpdate(prevProps){
if(this.props.selectedOrderState !== this.state.Order){
this.setState({
Order:this.props.selectedOrderState
});
}
}
handleChange = (e) => {
this.setState({
Order:{
...this.state.Order,
[e.target.id]: e.target.value,
}
})
};
handleSubmit = () => {
};
};
render() {
const order = this.props.selectedOrderState;
const { user: { credentials: { handle, imageUrl}}} = this.props;
return (
<form className='OrderInfo'onSubmit={this.handleSubmit}>
<div className='OrderInfoLbl'>Order Id:</div>
<div className="OrderInfoInput">{this.props.selectedOrderState.OrderId}</div>
<div className='OrderInfoLbl'>Delivery Date:</div>
<input className="OrderInfoInput" id="DeliveryDate" type="text" onChange=
{this.handleChange}></input>
<img className="ProfileBioSubmit" onClick={this.handleSubmit}
src="./images/svg/AcceptBtns.svg" alt="Edit"></img>
</form>
)
}
}
export default addOrder
Declare your state inside the constractor and bind your functions. I'm inviting you to take a look to forms docs with react
constructor(props) {
super(props);
this.state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
DeliveryDate:"",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
Probably the most hackerish way to do something but it worked:)
What i did was keep the componentDidUpdate() feeding the state to the child component but from the parent i passed down a function called handleChangeUP() for which i was able to use pass the event of onChange data through to change the original state selectedOrderState. Have a look!
Child
import React, { Component } from 'react'
export class addOrder extends Component {
state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
};
//Functs
componentDidUpdate(prevProps){
if(this.props.selectedOrderState !== this.state.Order){
this.setState({
Order:this.props.selectedOrderState
});
}
}
handleChange = (e) => {
this.props.handleChangeUP(e)
};
render() {
const order = this.props.selectedOrderState;
const { user: { credentials: { handle, imageUrl}}} =
this.props;
return (
<form className='OrderInfo'onSubmit={this.handleSubmit}>
<div className='OrderInfoLbl'>Order Id:</div>
<div className="OrderInfoInput">
{this.props.selectedOrderState.OrderId}</div>
<div className='OrderInfoLbl'>Delivery Date:</div>
<input className="OrderInfoInput" id="DeliveryDate" type="text"
value={this.state.Order.DeliveryDate}
onChange={this.handleChange}></input>
<img className="ProfileBioSubmit" onClick={this.handleSubmit}
src="./images/svg/AcceptBtns.svg" alt="Edit"></img>
</form>
)
}
}
export default addOrder
Parent
import React, { Component } from 'react'
import Child from './child'
export class Parent extends Component {
state = {
creating: false,//creat order window toggle
profiling: false,//Profile window toggle
chatting: false,//Chat window toggle
searching: false,//Search inside Chat window
selectedOrder: {}
};
handleChangeUP = (e) => {
console.log(e.target.id);
this.setState({
// [e.target.id]: e.target.value
//Order: e.target.value
selectedOrder:{
...this.state.selectedOrder,
[e.target.id]: e.target.value
}
})
}
render() {
return (
<div className="Wrapper">
<Child handleChangeUP={this.handleChangeUP}
selectedOrderState={this.state.selectedOrder}/>
</div>
)
}
}
export default Parent;

calling function in React SetState gives error that userName is unlabelled why?

import React,{Component} from 'react'
class Formhandler extends Component {
constructor(props) {
super(props)
this.state = {
userName:""
}
}
changer=(event)=>{
this.setState(()=>{
userName : event.target.value
})
}
render()
{
return(
<div>
<label>UserName</label>
<input type="text" value={this.state.userName} onChange={this.changer}/>
</div>
)
}
}
export default Formhandler
You are getting the error because of invalid syntax.
Update changer function
changer = (event) => {
this.setState({ userName: event.target.value });
};
You need to return an object inside the setState function but you are not that's the source of issue(syntax error).
use a function inside setState when your new state value would depend on your previous state value, where the function passed inside the setState will receive previous state as argument
changer = (e) => {
this.setState((prevState) => ({
userName : e.target.value
})
);
}
pass an object to update the state, use this when it doesn't depend on your previous state value.
changer = (e) => {
this.setState({ userName: e.target.value });
};
import React from "react";
class Formhandler extends React.Component {
constructor(props) {
super(props);
this.state = {
userName: "",
};
}
changer(event) {
this.setState(() => ({
userName: event.target.value,
}));
}
render() {
return (
<div>
<label>UserName</label>
<input
type="text"
value={this.state.userName}
onChange={this.changer.bind(this)}
/>
</div>
);
}
}
export default Formhandler;
It will work, compare your version and this

How to make an axios POST request in React?

So, some context: Users submit a dog name via a text input, and this is controlled by the 'Dogue.jsx' component:
import React from 'react';
class Dogue extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.id,
nameInput: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
nameInput: e.target.value,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.inputFunction(this.state.nameInput);
}
render() {
console.log(this.props.id);
return (
<div className="dogue-container">
<img className="img" src={this.props.dogList} />
<br />
<form onSubmit={this.handleSubmit} className="form">
<input
onChange={this.handleChange}
className="input"
type="text"
placeholder="Enter dog name"
/>
<br />
<button className="button">Submit</button>
</form>
<h2 className="text">Name: {this.props.name} </h2>
</div>
);
}
}
export default Dogue;
The submitted information is then passed to 'App.jsx', where it is used to update state:
import React, {Component} from 'react';
import './styles.css';
import DogList from './DogList';
import axios from 'axios';
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
};
this.updateStateWithInput = this.updateStateWithInput.bind(this);
}
setData = async () => {
const x = await fetch('https://dog.ceo/api/breed/hound/images');
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true,
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data,
});
}
updateStateWithInput(nameInput) {
//Here is where state is updated.
//change state, then use axios.post to submit data
}
render() {
return this.state.loading ? (
<h1 className="text"> Dogues Loading.....</h1>
) : (
<div>
<h1 className="text">Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction={this.updateStateWithInput}
/>
</div>
);
}
}
export default App;
The updated state, I imagine, will be used in the axios post request to submit data to the database. So, I've got input data being sent from Dogue to App, I'm just not sure what to do now? The information currently in state looks as follows:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
I should also show my map function, in DogList.jsx:
import React from 'react';
import Dogue from './Dogue';
const DogList = (props) => {
return (
<div className="img-container">
{props.dogs.map((doggie, index) => {
return (
<Dogue
id={props.name[index] && props.name[index].id}
key={index}
dogList={doggie}
name={props.name[index] && props.name[index].dogName}
inputFunction={props.inputFunction}
/>
);
})}
</div>
);
};
export default DogList;
You can send a POST request with axios by calling:
axios.post(url, data, options);
It’s similar to the way you called the get method to make a GET request.
I’m leaving this axios cheat sheet here since it’s really useful until you get the hang of it:
https://kapeli.com/cheat_sheets/Axios.docset/Contents/Resources/Documents/index

React ref state is null

I want to access the state of a Child component by using refs, but the state of the ref is always null.
In my React app, I have an Editor(basically, it is a form) that manipulates its own states, e.g. value change, update. The editor is used on multiple pages.
Editor.jsx
export default class Editor extends React.Component {
constructor(props) {
super(props);
this.state = {
value1: null,
... other values
};
}
onValue1Change = (e) => {
this.setState({value1: e.target.value});
}
onSave = (e) => {
// save values
}
render() {
return (
<div>
<input value={this.state.value1} onChange={this.onValue1Change}/>
... other input fields
<button onClick={this.onSave}>Save</button>
</div>
)
}
}
Now, there is a RegisterForm which covers all fields in the Editor. I made a small change in the Editor to hide the Save button so I can use it in the RegisterForm:
RegisterForm.jsx
export default class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.state = {
email: null,
firstname: null,
lastname: null
};
this.Editor = React.createRef();
}
onSave = (e) => {
let childState = this.Editor.current.state;
// childState is ALWAYS null!
}
render() {
return (
<div>
<input value={this.state.email} onChange={this.onEmailChange}/>
<input value={this.state.firstname} onChange={this.onFirstnameChange}/>
<input value={this.state.lastname} onChange={this.onLastnameChange}/>
...
<Editor ref={this.Editor} showSave={false}/>
...
<button onClick={this.onSave}>Save</button>
</div>
)
}
}
Turns out this.Editor.current.state is always null.
I have two questions.
Why this.Editor.current.state is null?
If I want to use props, how should I change my code? E.g. If I let RegisterForm pass props to Editor, I'd imagine something like this:
Editor.jsx
export default class Editor extends React.Component {
// same constructor
onValue1Change = (e) => {
this.setState({value1: e.target.value}, () => {
if(this.props.onValue1Change) this.props.onValue1Change(e);
});
}
// same render
}
RegisterForm.jsx
export default class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.state = {
email: null,
firstname: null,
lastname: null,
value1: null,
};
}
onValue1Change = (e) => {
this.setState({value1: e.target.value});
}
render() {
return (
<div>
<Editor showSave={false} onValue1Change={this.onValue1Change}/>
...
</div>
)
}
}
does it make the Child component render twice? Any suggestions on how to improve it?
You are passing the ref as a prop to the <Editor/> component but not doing anything with it after that.
For example:
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Receive props and ref through the forwardRef() callback parameter, then pass the ref to the child node.
This is called ref forwarding
I made a code sandbox for you to test it!

Why does the received state disappear in a moment?

I'm trying to dispatch actionC from TestClass so that Labelclass can receive the state change from reducer as below.
TestClass
class Test extends React.Component {
constructor(props){
super(props)
this.state = {text:props.text,onClick:props.onClick}
this.onInput = this.onInput.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onInput(e){
this.setState({text:e.target.value})
}
onSubmit(e){
this.state.onClick(this.state.text)
}
render(){
return <div>
<form onSubmit={this.onSubmit}>
<input value={this.state.text} onChange={this.onInput} />
<button type="submit">Add Todo</button>
</form>
</div>
}
}
function mapDispatchToProps_Test(dispatch,ownProps){
return {onClick:(id)=>dispatch(actionC(id))}
}
Test.propTypes = {
text:PropTypes.string.isRequired,
onClick:PropTypes.func.isRequired
}
Test = connect(null,mapDispatchToProps_Test)(Test)
LabelClass and Entry
class Label extends React.Component {
constructor(props){
super(props)
this.state = {text:props.text}
}
render(){
return <div> Hello<label>{this.props.text}</label></div>
}
}
function mapStateToProps_Label(state,ownProps){
return {
text:state.text
}
}
Label = connect(mapStateToProps_Label)(Label)
Label.propTypes = {
text:PropTypes.string.isRequired
}
const App = () =>(
<div>
<Test text="" onSubmit onClick />
<Label text=""/>
</div>
)
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById('root')
);
Action and Reducer
const CDD_TODO = 'CDD_TODO'
const {PropTypes} = React;
const {createStore} = Redux;
const { Provider, connect } = ReactRedux;
let store = createStore(reducer)
//action
function actionC(text) {
console.log(CDD_TODO)
return { type: CDD_TODO, text:text }
}
function reducer(state = {}, action) {
switch (action.type) {
case CDD_TODO:
console.log("action",action.text)
return Object.assign({}, state, {
text:action.text
})
default:
return state
}
}
The trouble is the output from LabelClass render() becomes invisible at once
after displayed in a moment.
I want it not to disappear. What is the cause?
You didn't map the value text from your reducer you created but you mapped the reducer it self. In your case, you have to map the text value from the reducer named text:
function mapStateToProps_Label(state,ownProps){
// state.text is the state of your reducer
// state.text.text is one of the state value
return {
text:state.text.text
}
}
Besides, from what I see, you needn't a state in Label:
class Label extends React.Component {
render(){
return <div> Hello<label>{this.props.text}</label></div>
}
}
Same thing in Test: for onClick on this.state is useless:
class Test extends React.Component {
constructor(props) {
super(props);
this.state = { text: props.text }
this.onInput = this.onInput.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onInput(e) {
this.setState({ text: e.target.value });
}
onSubmit(e) {
this.props.onClick(this.state.text);
}
render() {
return <div>
<form onSubmit={this.onSubmit}>
<input value={this.state.text} onChange={this.onInput} />
<button type="submit">Add Todo</button>
</form>
</div>;
}
}
I think you should put a breakpoint in mapStateToProps to see if text si modified after having been set. You should put a break point in the reducer to see if an action dispatch an action that erase the text data.

Resources