Как я могу перенаправить после успешной отправки формы с помощью react-redux

#javascript #reactjs #redux #react-redux #redux-thunk

#javascript #reactjs #redux #react-redux #redux-thunk

Вопрос:

action.js

 import axios from 'axios';
import { EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from '../constraints/eventConstraint';

const addEvent = (event) => async (dispatch) => {
 dispatch({ type: EVENT_ADD_REQUEST, payload: event });
 try {
    const { data } = await axios.post(`http://localhost:4000/event`, event);
    dispatch({ type: EVENT_ADD_SUCCESS, payload:data });
 }
 catch (error) {
    dispatch({ type: EVENT_ADD_FAIL, payload:error.message });
 };
};


export { addEvent };
  

constraint.js

 export const EVENT_ADD_REQUEST = 'EVENT_ADD_REQUEST';
export const EVENT_ADD_SUCCESS = 'EVENT_ADD_SUCCESS';
export const EVENT_ADD_FAIL = 'EVENT_ADD_FAIL';
  

reducer.js

 import {EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from "../constraints/eventConstraint";

function eventAddReducer(state = {}, action) {
 switch(action.type) {
    case EVENT_ADD_REQUEST:
        return { loading: true };
    case EVENT_ADD_SUCCESS:
        return { loading: false, event: action.payload, success:true };
    case EVENT_ADD_FAIL:
        return { loading: false, error: action.payload, success:false };
    default:
        return state
 };
};

export { eventAddReducer }
  

store.js

 import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { eventAddReducer } from './reducers/eventReducer';

const initialState = {};
const reducer = combineReducers({
  addEvent: eventAddReducer
});

const store = createStore(reducer, initialState, compose(applyMiddleware(thunk)));
export default store
  

event.js

 import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';

const AddEvent = () => {
 const history = useHistory();
 const [event, setEvent] = useState();
 const addNewEvent = useSelector(state => state.addEvent);
 console.log(addNewEvent)
 const dispatch = useDispatch();

 const handleChange = e => {
    setEvent({ ...event,[e.target.name]:e.target.value})
 };

 const submitHandler = async (e) => {
    e.preventDefault();
    await dispatch(addEvent(event));
 };

// if(addNewEvent.success === true) {   
//     history.push('/')
// };   ===========>>>>>>>>>>> It works at first but after submission first time next time it automatically redirects to '/' because react-redux holds state

return (
    <>
        <form onSubmit = { submitHandler } >
            <div className="form-group">
                <label htmlFor="name">Name:</label>
                <input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
            </div>
            <div className="form-group">
                <label htmlFor="description">Description:</label>
                <input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
            </div>
            <div className="form-group">
                <label htmlFor="price">Price:</label>
                <input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
            </div>
            <Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
            <button type="submit" className="btn btn-success float-right"> Add Event </button>
        </form>
    </>
 )
};

export default AddEvent
  

Все работает нормально, но я хочу, чтобы после успешной отправки формы ее нужно перенаправить на какую-либо страницу. Это просто, без react-redux мы можем просто перенаправить после отправки формы, но я пытаюсь изучить redux и мало что знаю о redux. Я попытался использовать success = true в reducer, он работает с первого раза, но поскольку redux сохраняет состояние, когда я пытаюсь открыть ссылку, он автоматически перенаправляет на домашнюю страницу, поскольку success = true удерживается react-redux. Любая помощь будет оценена

Комментарии:

1. вы могли бы использовать промежуточное программное обеспечение, которое ищет определенный ключ в отправленном объекте действия и запускает перенаправление маршрута, когда находит его.

2. не могли бы вы подробнее рассказать

Ответ №1:

Во-первых: убедитесь, что вы выполнили сброс success для каждого действия:

 function eventAddReducer(state = {}, action) {
  switch(action.type) {
     case EVENT_ADD_REQUEST:
         return { 
           loading: true,
           success: null // <-- Look at this
          };
    /** ... */
  };
};
  

Второе: подключите success переменную хранилища к вашему компоненту и проверьте ее в componentDidupdate событии, подобном:

 import { connect } from 'react-redux';

class AddEvent extends React.Component {
  componentDidUpdate(prevProps) {
    const {success} = this.props;
    const {succcess: prevSuccess} = prevProps;

    if (success amp;amp; success !== prevSuccess) {
      /** Redirect here */
    }
  }
  /** .... */
}

const mapStateToProps = ({ addEvent: { success } }) => ({
  success
});

export default connect(mapStateToProps)(AddEvent);
  

Использование перехватов

 const AddEvent = ({ success }) => {
  useEffect(() => {
    if (success) {
      /** Redirect here */
    }
  }, [success]); // <-- This will make sure that the effect only runs when success variable has changed
};

const mapStateToProps = ({ addEvent: { success } }) => ({
  success
});

export default connect(mapStateToProps)(AddEvent);
  

Комментарии:

1. но я хочу использовать функциональный компонент, а не компонент класса

2. Он по-прежнему не работает, так как после успешного перехода в true он не отображается, пока мы не отправим форму

Ответ №2:

Сейчас я столкнулся с той же проблемой и решил ее двумя способами

  1. Первое: завершить ваше решение в

event.js файл:

 import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';

const AddEvent = () => {
 const history = useHistory();
 const [event, setEvent] = useState();
 const addNewEvent = useSelector(state => state.addEvent);
 console.log(addNewEvent)
 const dispatch = useDispatch();

 const handleChange = e => {
    setEvent({ ...event,[e.target.name]:e.target.value})
 };

 const submitHandler = async (e) => {
    e.preventDefault();
    await dispatch(addEvent(event));
 };
addNewEvent.success amp;amp; history.push('/')

return (
    <>
    // after submition success only you will redirect to "/"

    {addNewEvent.success amp;amp; history.push('/')}
    
        <form onSubmit = { submitHandler } >
            <div className="form-group">
                <label htmlFor="name">Name:</label>
                <input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
            </div>
            <div className="form-group">
                <label htmlFor="description">Description:</label>
                <input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
            </div>
            <div className="form-group">
                <label htmlFor="price">Price:</label>
                <input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
            </div>
            <Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
            <button type="submit" className="btn btn-success float-right"> Add Event </button>
        </form>
    </>
 )
};

export default AddEvent
  

мы можем получить доступ к значению успеха из редуктора хранилища только после возврата, а не раньше, поэтому вы можете получать доступ к значению при каждом повторном рендеринге и перенаправлении в зависимости от вашего состояния

теперь react-router-dom v6 вы можете использовать useNavigate() и вносить изменения в нижеприведенные строки

 import { Link, useNavigate } from "react-router-dom";
// rest of imports

const AddEvent = () => {
  const navigate = useNavigate();

//rest of code
return (
<>
{addNewEvent.success amp;amp; navigate('/')}

//rest of code

</>

)
};
export default AddEvent
  
  1. Второе: вы можете создать условие action.js , отправив navigate в качестве аргумента при отправке действия и написать свое условие после успешной отправки, как показано ниже

event.js файл

 import { Link, useNavigate } from "react-router-dom";
// rest of imports

const AddEvent = () => {
 const navigate = useNavigate();

const submitHandler = async (e) => {
   e.preventDefault();
   await dispatch(addEvent(event,navigate));
};

//rest of code
return (
<>

//rest of code

</>

)
};
export default AddEvent
  

и в action.js файле

 import axios from 'axios';
import { EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from '../constraints/eventConstraint';

const addEvent = (event,navigate) => async (dispatch) => {
 dispatch({ type: EVENT_ADD_REQUEST, payload: event });
 try {
    const { data } = await axios.post(`http://localhost:4000/event`, event);
    dispatch({ type: EVENT_ADD_SUCCESS, payload:data });
    
    //add your navigation or condition here
    navigate("/");

 }
 catch (error) {
    dispatch({ type: EVENT_ADD_FAIL, payload:error.message });
 };
};


export { addEvent };
  

Ответ №3:

Я знаю, что это не самое идеальное решение, но как насчет создания действия, которое сбросит успех и отправит его внутри useEffect?

Что-то вроде этого:

Редуктор

 import {EVENT_ADD_FAIL, EVENT_ADD_REQUEST, EVENT_ADD_SUCCESS } from "../constraints/eventConstraint";

function eventAddReducer(state = {}, action) {
 switch(action.type) {
    case EVENT_ADD_REQUEST:
        return { loading: true };
    case EVENT_ADD_SUCCESS:
        return { loading: false, event: action.payload, success:true };
    case EVENT_ADD_FAIL:
        return { loading: false, error: action.payload, success:false };
case RESET:
      return {
         ...state, 
         loading: false, 
         success:false
      } // This will reset everything including success
    default:
        return state
 };
};

export { eventAddReducer }

  

и в вашем event.js файл вызывает действие, которое отправит сброс. Убедитесь, что вы поместили его внутри useeffect .

 
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { addEvent } from '../actions/eventAction';

const AddEvent = () => {
 const history = useHistory();
 const [event, setEvent] = useState();
 const addNewEvent = useSelector(state => state.addEvent);
 console.log(addNewEvent)
 const dispatch = useDispatch();
React.useEffect(() =>{
myResetAction()

}, [])
 const handleChange = e => {
    setEvent({ ...event,[e.target.name]:e.target.value})
 };

const submitHandler = async (e) => {
    e.preventDefault();
    await dispatch(addEvent(event));
 };

// if(addNewEvent.success === true) {   
//     history.push('/')
// };   ===========>>>>>>>>>>> It works at first but after submission first time next time it automatically redirects to '/' because react-redux holds state

return (
    <>
        <form onSubmit = { submitHandler } >
            <div className="form-group">
                <label htmlFor="name">Name:</label>
                <input type="text" className="form-control" id="name" name="name" onChange={e => handleChange(e)} />
            </div>
            <div className="form-group">
                <label htmlFor="description">Description:</label>
                <input type="text" className="form-control" id="description" name="description" onChange={e => handleChange(e)} />
            </div>
            <div className="form-group">
                <label htmlFor="price">Price:</label>
                <input type="text" className="form-control" id="price" name="price" onChange={e => handleChange(e)} />
            </div>
            <Link to='/'> <button type="button" className="btn btn-success"> Back </button> </Link>
            <button type="submit" className="btn btn-success float-right"> Add Event </button>
        </form>
    </>
 )
)}
  

Это поможет.