Arguments are not passing from component to actions when using redux-saga - reactjs

I am using Redux-saga, so that I can fetch items from my API side. I have initialized my saga middleware in the index page as per the instruction given in the redux-saga documentation.
The following snippet is my /src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducers';
import App from './root-components/app';
export const sagaMiddleware = createSagaMiddleware()
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
ReactDOM.render(
<Provider store={store}>
<div>
<App />
</div>
</Provider>,
document.getElementById('root')
);
My component is src/component/index.jsx
import React, { Component } from 'react';
import { sagaMiddleWare } from '../index.js';
import { fetchPrescriptionsFromUrl } from '../actions/presAction';
class Index extends Component {
componentWillMount(){
const res = "434566ytui87unhygt99";
sagaMiddleware.run(fetchPrescriptionsFromUrl);
fetchPrescriptionsFromUrl(res);
}
render(){
return(
<div>
<h1> My First Component </h1>
</div>
)
}
}
export default Index;
I am calling action fetchPrescriptionsFromUrl(res) passing the parameter res along with it. My action is presAction is
import {call, put} from "redux-saga/effects";
import axios from 'axios';
const getCharacters = (accessPoint) => {
axios.get(accessPoint)
}
export function *fetchPrescriptionsFromUrl(accessToken){
console.log('AT', accessToken)
try {
const response = yield call(getCharacters(accessToken))
} catch (e) {
console.log(e)
}
}
On console.log of argument which is been passed from the component index.jsx is stated as undefined.
I am beginner in redux-saga, don't know where am making error. It will be helpful if I got any solutions for this problem.

Redux Saga call function cannot be used this way. It should look like this
const response = yield call(getCharacters, accessToken)
In your case getCharacters was evaluated imidiatelly and it's result was passed into call
Also you shouldn't use sagaMiddleWare in your Index component. Point of (not just saga) middleware is that you dispatch actions as normal and then catch concrete actions in your middleware

Related

Redux saga called multiple times (2 or 3 times) Nextjs

I am using Redux and Redux saga with Nextjs, i wrapped the store on _app.js file and when i call the api with post or get requests Redux-Saga is getting called at least two times, specially for post requests for example if i want to register a user using the api it is registering the user two times on the database
PS: I am using rootSaga and i am not calling a saga twice there
This is my store file:
import { createStore, applyMiddleware, compose } from "redux";
import createSagaMiddleware from "redux-saga";
import reducers from "./reducers";
import sagas from "./sagas";
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
export function configureStore(initialState) {
const store = createStore(
reducers,
initialState,
compose(applyMiddleware(...middlewares))
);
sagaMiddleware.run(sagas);
if (module.hot) {
module.hot.accept("./reducers", () => {
const nextRootReducer = require("./reducers");
store.replaceReducer(nextRootReducer);
});
}
return store;
}
export const wrapper = createWrapper(configureStore, { debug: true });
And this is my _app.js file
import "../styles/styles.global.scss";
import "../styles/Home.module.scss";
import React from "react";
import App, { Container } from "next/app";
import { Provider, connect } from "react-redux";
import withRedux from "next-redux-wrapper";
import { configureStore, wrapper } from "../libs/store";
const context = React.createContext(undefined);
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { ...pageProps };
}
render() {
const { Component, pageProps, router } = this.props;
return (
<Provider store={configureStore()} context={context}>
<Component {...pageProps} key={router.asPath} />
</Provider>
);
}
}
export default wrapper.withRedux(MyApp);
Thank you.
I Fixed it by removing the provider from _app.js and deleting _document.js
PS: This solutions is for Nextjs >= 10.0.0

Post an item using react js

My application make a post request to an api. The issue is that the post request does not work properly.
Now the data from :
const postData = () => {
postMessageNew({
first: 1,
second: 2
});
};
... riches till :
export const postMessageNew = newMessage => {
console.log(newMessage);
return {
type: POST_MESSAGE,
newMessage
};
};
So, the data after clicking button, reaches only till above console.log, not till post request from saga file. What could be the problem? And how to fix the code? Why the values don't appear in the code bellow?
function* postNewMessage(newMessage) {
console.log(newMessage);....
Demo: https://codesandbox.io/s/upbeat-franklin-g8x3o?file=/src/App.js:129-219
You need to set up Redux properly, including a reducer, and use the saga middleware to run your saga:
import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import rootSaga from "./sagas";
import rootReducer from "./Reducer";
import App from "./App";
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Check out a working example: https://codesandbox.io/s/happy-khayyam-uy06r?file=/src/App.js

How to apply async react redux middleware

I'm a beginner in react.
I'd like to use the react redux to request api.
Error: Actions must be plain objects. Use custom middleware for async actions. An error has occurred.
Please help me with any problems.
I'd like to ask you how redux middleware should be applied.
action/index.js
export const fetchActionMovies = async () => {
const request = await axios.get(`${BASE_URL}/discover/movie?api_key=${API_KEY}&with_genres=28`)
return {
type: FETCH_ACTION_MOVIES,
payload: request
}
}
reducers/reducerActionMovies.js
import { FETCH_ACTION_MOVIES } from '../actions/index';
export default function (state = {}, action) {
switch (action.type) {
case FETCH_ACTION_MOVIES:
const data = action.payload.data.results;
return { ...state, data }
default:
return state;
}
}
container/ActionMovie.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchActionMovies } from '../store/actions/index';
const ActionMovies = () => {
const dispatch = useDispatch();
const fetch = dispatch(fetchActionMovies());
console.log(fetch);
return (
<div>
<h1>Action Movies</h1>
</div>
)
}
export default ActionMovies;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import ReduxThunk from 'redux-thunk';
import rootReducer from './store/reducers';
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(ReduxThunk))
);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
Error: Actions must be plain objects. Use custom middleware for async actions.
First, even if you're going to use redux-thunk, you need to break up your action into three parts to track the asynchronous state of the request.
const FETCH_ACTION_MOVIES_REQUEST = "FETCH_ACTION_MOVIES_REQUEST";
const FETCH_ACTION_MOVIES_SUCCESS = "FETCH_ACTION_MOVIES_SUCCESS";
const FETCH_ACTION_MOVIES_FAILURE = "FETCH_ACTION_MOVIES_FAILURE";
You should create three actions that use these types that your reducer will track. Now, if you're not going to use redux-thunk... you will need to perform this fetch in your component. However, if you are using redux-thunk you can create an action like this:
export const fetchActionMovies = () => dispatch => {
dispatch(fetchActionMoviesRequest());
return axios.get(`${BASE_URL}/discover/movie?api_key=${API_KEY}&with_genres=28`).then(({
data
}) => {
dispatch(fetchActionMoviesSuccess(data));
}).catch(error => {
dispatch(fetchActionMoviesFailure(error));
})
}
Another option to consider is redux-saga.

Action Creator and Reducer not console.logging, but the component is connected

I am trying to connect a React component to Action creator and dispatch action to reducers and render data. I am using Redux as a state management.
I am not sure why it isn't console logging, because it shows I connected action creator fine.
Also I am wondering would you rather use redux promise as middleware or redux thunk?
SearchBar.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchArtist } from './actions';
class SearchBar extends Component{
constructor(){
super();
this.state = {
query:'test'
}
this.handleChange = this.handleChange.bind(this);
this.clearForm = this.clearForm.bind(this);
// this.searchArtist = this.searchArtist.bind(this);
}
clearForm(e){
this.setState({
query:''
})
}
handleChange(e){
this.setState({
query:e.target.value
});
}
render(){
console.log(this.props)
return (
<div>
<form onSubmit={this.props.searchArtist}>
<label>Search For Your Favourite Song</label>
<input type="text" value={this.state.query} onClick={this.clearForm} onChange={this.handleChange}/>
<input type="submit" value="Submit"/>
</form>
</div>
)
}
}
function mapStateToProps(state){
return{
search:state.search
}
}
export default connect(mapDispatchToProps, mapStateToProps)(SearchBar);
Action Creator
index.js
import axios from 'axios';
import { FETCH_API } from './types';
export function searchArtist(term){
const request = axios.get(`https://api.spotify.com/v1/artists/{term}`);
return{
type: FETCH_API,
payload:request
}
}
store
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import reducers from './reducers';
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
const store = createStore(reducers, {}, applyMiddleware(reduxThunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
registerServiceWorker();
connect statement should be
export default connect(mapStateToProps,mapDispatchToProps)(SearchBar);
As mapDispatchToProps is specified in wrong order, there is not function/action mapped to the container.As a result no action is called, so no reducers. Hense, action & reducers not logging anything.
Adding more to Riyaj Khan's answer, that you should correct the syntax of connect by interchanging the position of mapStateToProps and maoDispatchToProps, you have to dispatch something intto redux. Even if you are using promise middleware you will have to dispatch the promise.
In your case searchArtist() is the dispatching function in you intend to use, but it should be defined inside the following function.
eg,
function mapDispatchToProps(dispatch) {
return({
searchArtist: () => {dispatch({type:"SEARCH", payload:new Promise()})}
})
}
I'd recommend cleaning up the action creator. just passing in the request is a bit unpredictable,
could do something like this
export function fetchArtist(term){
fetch(`${API_URL}/${term}`)
.then((response) => {
if(!response.ok) {
throw Error(response.statusText);
}
return response;
}).then((data) => data.json())
.then((artists) => dispatch(termSuccess(artists)))
.catch((err) => dispatch(termError(err)))
};
This is using redux thunk since I'm able to dispatch using a function. So with the other action creators, you'll end up making a termSuccess, termLoading, termError. Helps keep things a bit more organized instead of placing it all in one action creator.

Redux - reducer not getting called

I'm trying to learn Redux. I followed an online tutorial and used their example and it worked fine. Then I made another small test app and it worked fine. Now I'm trying another small test app and I'm stuck on this error and have no idea why.
The app is simply a input box and a button, and when you click the button it adds whatever's in the box to a list that is displayed below.
However, the reducer isn't updating the state. I've looked at the common problems that cause this and can't seem to find those errors in mine, my reducer doesn't change the state and I've bound the dispatch and so on. Maybe I've just mispelled something somewhere (or something small and stupid like that) but I just can't get it working.
So I tried to change it so that it just displays whatever you type in the box below and it STILL doesn't work. Anyone know why the reducer isn't activating?
index.js (main)
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import createLogger from 'redux-logger';
import allReducers from './reducers';
import App from './components/App';
const logger = createLogger();
const store = createStore(
allReducers,
applyMiddleware(thunk, promise, logger)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App.js
import React from 'react';
import NameList from '../containers/name-list.js'
import Input from '../containers/input.js'
const App = () => (
<div>
<Input />
<hr />
<NameList />
</div>
);
export default App;
index.js (action)
export const addName = (name) => {
return {
type: 'NAME_ADDED',
payload: name
}
};
reducer-add-name.js
export default function (state = null, action) {
switch (action.type) {
case 'NAME_ADDED':
return action.payload;
break;
}
return state;
}
index.js (reducer combiner)
import {combineReducers} from 'redux';
import AddNameReducer from './reducer-add-name';
const allReducers = combineReducers({
nameList: AddNameReducer
});
export default allReducers
input.js
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {addName} from '../actions/index'
class Input extends Component {
render() {
return(
<div>
<input id='name-input' />
<button id='add-name-button' onClick={() => addName(document.getElementById('name-input').value)}>Add name</button>
</div>
);
}
}
function mapStateToProps(state) {
return {
nameList: state.nameList
};
}
function matchDispatchToProps(dispatch) {
return bindActionCreators({addName: addName}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Input);
name-list.js
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
class NameList extends Component {
getNameList() {
//This is where I'll loop through this.props.nameList and build the list of elements
return this.props.nameList;
}
render() {
return(
<div>Current list : {this.getNameList()}</div>
);
}
}
function mapStateToProps(state) {
return {
nameList: state.nameList
};
}
export default connect(mapStateToProps)(NameList);
Thanks for any help!
I think your action hasn't been dispatched.
In your input.js
On button tag, change
onClick={() => addName(document.getElementById('name-input').value)}
to
onClick={() => this.props.addName(document.getElementById('name-input').value)}
Thing is action should be passed through mapDispatchToProps and 'bindActionCreators(actions, dispatch)' will wrap your action with 'dispatch', and pass through your component via props 'addName'. (as defined in your mapDispatchToProps)
An action alone is like a bullet (just return object) you will need something to fire it (dispatch)
In your case
import {addName} from '../actions/index' <--- a bullet
has been declared and your onClick, without dispatch, it would only return a mere object, not dispatching it to reducer.
addName() // <--- Just a bullet
But
this.props.addName() // <--- bullet with a gun
is from mapDispatchToProps / bindActionCreators... it has dispatch() wrapped around, that is the one we would use.
You should combine existing state with the new action payload in reducer-add-name.js instead of just returning the payload i.e.
return Object.assign({}, state, {
todos: [
...state,
{
action.payload
}
]
})
Not the OP's question but for people who just searched Google for "reducer not getting called", make sure you call your action creator:
dispatch(action())
not
dispatch(action)
You have to pass in the action, not the action creator. With useSelect in Redux Toolkit I usually don't have parens in the arg so I forgot to put the parents in the call to dispatch.
This happened to me today.
I forgot to add my new action to my destructured props in React while using the class method
const { newAction } = this.props

Resources