I have a function in src/actions/index.js which is fetching data from an external api using axios. It uses a dispatch method and sets the payload as the data fetched from the api.
I then import the function in my src/App.js like so:
import {fetchPosts} from "./actions/index.js";
fetchPosts then gets passed as a prop to the Button component, along with configureButton.buttonText (gets correctly passed), which when clicked should trigger the function to fetch the api; however, it does not run.
const App = ({ posts }) => {
const configureButton = {
buttonText: "Get Posts",
// this is where the fetchPosts f() gets set as the emitEvent prop
emitEvent: fetchPosts,
};
return (
<div className="App">
<Header />
<section className="main">
//destructure the configureButton object
<Button {...configureButton} />
</section>
</div>
)
}
const mapStateToProps = state => {
return {
posts: state.posts,
};
};
export default connect(mapStateToProps, { fetchPosts })(App);
Button.js component:
const Button = ({ buttonText, emitEvent}) => {
const submitEvent = () => {
if(emitEvent){
emitEvent()
}
}
return (
<button onClick={submitEvent} data-test="buttonComponent">{buttonText}</button>
)
}
export default Button
Why is the fetchPosts function not being called?
Related
I have this handleSubmit that returns me a key (verifyCode) that I should use in another component. How can I pass this verifyCode to another component?
const SendForm = ({ someValues }) => {
const handleSubmitAccount = () => {
dispatch(createAccount(id, username))
.then((response) => {
// I get this value from data.response, its works
const { verifyCode } = response;
})
.catch(() => {
});
};
return(
//the form with handleSubmitAccount()
)
}
export default SendForm;
The other component is not a child component, it is loaded after this submit step. But I don't know how to transfer the const verifyCode.
This is the view where the components are loaded, it's a step view, one is loaded after the other, I need to get the const verifyCode in FormConfirmation
<SendForm onSubmit={handleStepSubmit} onFieldSubmit={handleFieldSubmit} />
<FormConfirmation onSubmit={handleStepSubmit} onFieldSubmit={handleFieldSubmit} />
Does anyone know how I can do this?
You need to move up the state to a component that has both as children and then pass down a function that updates as a prop
import React from "react";
export default function App() {
const [value, setValue] = React.useState(0);
return (
<div className="App">
<Updater onClick={() => setValue(value + 1)} />
<ValueDisplay number={value} />
</div>
);
}
const Updater = (props) => <div onClick={props.onClick}>Update State</div>;
const ValueDisplay = (props) => <div>{props.number}</div>;
Check out the docs here
For more complex component structures or where your passing down many levels you may want to look into reactContext
import React from "react";
//Set Default Context
const valueContext = React.createContext({ value: 0, setValue: undefined });
export default function App() {
const [value, setValue] = React.useState(0);
return (
<div className="App">
{/** Pass in state and setter as value */}
<valueContext.Provider value={{ value: value, setValue }}>
<Updater />
<ValueDisplay />
</valueContext.Provider>
</div>
);
}
const Updater = () => {
/** Access context with hook */
const context = React.useContext(valueContext);
return (
<div onClick={() => context.setValue(context.value + 1)}>Update State</div>
);
};
const ValueDisplay = () => {
/** Access context with hook */
const context = React.useContext(valueContext);
return <div>{context?.value}</div>;
};
I'm making a project with an API call, and so far I've been able to pass the static data(for know I'll keep it hard coded) and then console.log the data provided by the static data, but I can't store it in my state, I can just console.log it and I dont know why. The following error happens in my console:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Here's the code, I think you guys will see what I'm doing in a better way:
import React, { useState } from "react";
import "./styles.css";
import TopList from "./components/TopList";
export default function App() {
const [state, setState] = useState({
data: [23251319, 23251742, 23251158, 2423431],
results: []
});
const fetcher = id => {
fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)
.then(res => res.json())
.then(data => {
console.log(data);
setState({
results: data
});
});
};
return (
<div>
<TopList data={state.data} fetcher={fetcher} />
</div>
);
}
import React from "react";
import Top from "./Top";
function TopList({ data, fetcher }) {
const mapped = data.map(item => (
<Top fetcher={fetcher} id={item} key={item} />
));
return <div>{mapped}</div>;
}
export default TopList;
import React from "react";
function Top({ id, fetcher }) {
fetcher(id);
return (
<div>
<h1>Hello from top</h1>
</div>
);
}
export default Top;
You should be fetching the data after the component has mounted inside a componentDidMount() lifecycle method or since you are using functional components you can use the useEffect() hook.
Secondly you are prop drilling the fetcher to the Top component for no reason.
If the Top component fetches the data, it should be responsible for calling the fetcher inside a useEffect() hook.
For example
in your app component
export default function App() {
const [ids, setIds] = useState([23251319, 23251742, 23251158, 2423431]);
return (
<div>
<TopList idArray={ids}/>
</div>
);
}
in TopList
function TopList({ idArray }) {
return (
<div>
{
idArray.map((id) => (<Top id={id} key={id}/>))
}
</div>;
}
In Top Component
function Top({ id }) {
const [state, setState] = useState({results: null, error: undefined})
useEffect(() => {
const fetcher = id => {
fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)
.then(res => res.json())
.then(data => {
console.log(data);
// if fetch success
setState({
results: data,
error: undefined
});
})
.catch(error => {
// if error set results to null and error to the error that happened
// during fetch
setState({results: null, error: error})
});
};
// finally call the fetcher with the id
fetcher(id);
}, [id])
return (
<div>
<h1>Hello from top</h1>
<pre>{state.results && JSON.stringify(state.results, null, 2)}</pre>
</div>
);
}
I'm new to redux, and I can find lots of info on how to pass a redux state to the component, but not the other way round, so I'm not sure if I'm searching the correct vocabulary. But Essentially I want to be able to reference the current state of a react component in a redux helper function, this is what I've done - and I'm getting TypeError: dispatch is not a function and handleSubmit is just launching as soon as the page is loaded:
App.js
render() {
return (
<div className="App">
<p>{this.state.id}</p>
<form onSubmit={this.handleSubmit(this.state.id)}>
<button type="submit">Submit</button>
</form>
</div>
)
}
const mapDispatchToProps = dispath => bindActionCreators({
handleSubmit
}, dispath);
export default connect(
mapDispatchToProps
)(App);
reducers.js
export const handleSubmit = (test) => {
window.open("http://localhost:5000/"+test);
}
//Reducer
export default (state = [], action) => {
switch (action.type) {
default:
return state;
}
};
First, you don't use the function that react-redux pass through the props and try to call handleSubmit on the component itself.
You are also calling the function inside onSubmit immediately instead of passing a reference to a function so wrap it in an arrow function and use handleSubmit from this.props
onSubmit={() => this.props.handleSubmit(this.state.id)}
Second, the first argument to connect is the mapping function to get a slice of the state called mapStateTpProps by convention, pass in null as the first argument.
there is also no need to use bindActionCreators and you can just pass an object with functions and react-redux will wrap them in dispatch for you
export default connect(
null,
{ handleSubmit }
)(App);
You need to put id to the state of App and manage it through redux.
Code below will help you.
// App.js
render() {
return (
<div className="App">
<p>{this.props.id}</p>
<form onSubmit={this.props.ActionSubmit(this.props.id)}>
<button type="submit">Submit</button>
</form>
</div>
)
}
const mapStateToProps = store => ({
id: store.appReducer.id,
})
const mapDispatchToProps = dispath => bindActionCreators({
ActionSubmit
}, dispath);
export default connect(
mapStateToProp,
mapDispatchToProps
)(App);
// reducers.js
export ACTION_SUBMIT = 'ACTION_SUBMIT'
export const ActionSubmit = id => ({
type: ACTION_SUBMIT,
payload: {
id,
}
})
const initialState = {
id: 0,
}
const doSubmit = (id) => {
window.open("http://localhost:5000/"+id);
}
export default AppReducer(state = initialState, action) {
switch(action.type) {
case ACTION_SUBMIT:
doSubmit( action.payload.id)
return {
id: action.payload.id,
}
default:
return state
}
}
I'm new to React. I'm displaying a list of songs and I want to allow the user to add songs to their favourites. I'm using Redux to store the favourited songs. My PlayList component looks like:
import AddSong from '../containers/AddSong'
class Playlist extends Component {
render(){
return (
<div>
<h1>Playlists</h1>
<ul className="container">
{this.state.items.map(item =>
<li key={item.track.id}>
{item.track.name} by {item.track.artists[0].name}
<img src={item.track.album.images[0].url} height="150" width="150" />
<AddSong title={item.track.name} />
</li>
)}
</ul>
</div>
);
}
...
}
So I passing the song name to AddSong with <AddSong title={item.track.name} />
And AddSong looks like:
import React from 'react'
import { connect } from 'react-redux'
import { addSong } from '../actions'
let AddSong = ({ dispatch }) => {
let input
console.log('this is ', this);
return (
<div>
<form
onSubmit={e => {
e.preventDefault()
// GET SONG FROM PROPS AND DISPATCH
//dispatch(addSong(input.value))
}}
>
<input
ref={node => {
input = node
}}
/>
<button type="submit">
Add Song
</button>
</form>
</div>
)
}
AddSong = connect()(AddSong)
export default AddSong
However, this is an object with the property:
{
a: Connect(props, context)
}
How do I get the song title in AddSong?
EDIT
So this is what I have now, Im passing the song title to AddSong here:
<AddSong song={item.track.name} title={item.track.name} />
I'm passing the song title in as song and title to show what happens.
In AddSong, I have:
const mapStateToProps = (state) => {
const {song:song} = state; // or whatever the reducer called
return {song};
};
const mapDispatchToProps = (dispatch) => ({
addSong: (value) => dispatch(addSong(value)),
});
export default connect(mapStateToProps, mapDispatchToProps)(AddSong);
And at the top of AddSong I'm doing:
let AddSong = ({ dispatch, ...props }) => {
let input
console.log('props is ', props);
The console outputs:
props is Object {song: undefined, title: "Young Blood"}
I've changed the button to:
<button onClick={(value)=>props.addSong(value)}>
Add Song
</button>
When I click, this gives the error:
Failed prop type: Invalid prop `songs[0].text` of type `object` supplied to `SongList`, expected `string
Try to use this function
const mapStateToProps = function(store) {
return {
data: store.data
};
}
AddSong = connect(mapStateToProps)(AddSong)
I assume you have the reducer and you want to access the state via props, if this the case you can mapStateToProps e.g.
const mapStateToProps = (state) => {
const {song:song} = state; // or whatever the reducer called
return {song};
};
const mapDispatchToProps = (dispatch) => ({
addSong: (value) => dispatch(addSong(value)),
});
export default connect(mapStateToProps, mapDispatchToProps)(AddSong);
then you can just write this.props.song.
Long post below, but not complicated!
I have setup my form:
NewCommentForm Component
class NewCommentForm extends Component {
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit}>
<Field component="input" type="text" name="title"/>
<Field component="textarea" type="text" name="content"/>
</form>
)
}
}
const mapStateToProps = (state) => ({})
// Actions are imported as 'import * as action from '../actions/comments'
NewCommentForm = connect(mapStateToProps, actions)(NewCommentForm)
NewCommentForm = reduxForm({
form: 'newComment',
onSubmit: actions.postComment // This is the problem!
})(NewCommentForm);
RemoteSubmitButton Component
class RemoteSubmitButton extends Component {
render() {
const { dispatch } = this.props;
return (
<button
type="button"
onClick={() => dispatch(submit('newComment'))}>Submit</button>
)
}
}
RemoteSubmitButton = connect()(RemoteSubmitButton);
Everything wrapped in NewComment Component:
class NewComment extends Component {
render() {
return (
<div className="new-comment">
<NewCommentForm />
<RemoteSubmitButton />
</div>
)
}
}
The problem is with the postComment function:
export const postComment = (comment) => {
console.log("Post comment - first;") // THIS ONE GETS CALLED
return (dispatch) => {
console.log("Post comment - second"); // THIS ONE IS NEVER CALLED
return api.postComment(comment).then(response => {
dispatch({
type: 'POST_COMMENT_SUCCESS',
response
});
});
}
}
that gets its api.postComment from another file:
export const postComment = (comment) => {
return axios.post(post_comment_url, {
comment
}).then(response => {
return response;
});
}
I have redux-thunk setup in my store:
import thunk from 'redux-thunk';
const configureStore = (railsProps) => {
const middlewares = [thunk];
const store = createStore(
reducers,
railsProps,
applyMiddleware(...middlewares)
);
return store;
};
Why after submitting the form using the RemoteSubmitButton the second part of the postComment function is never called? What did I do wrong?
The problem is because you are trying to use the action that is not connected with the react-redux connect. You have to use it inside the component that is connected to the redux.