skip to Main Content

I am working with react-redux and I used routers turn pages to detail.js when I put the todo list.

It should have ownprops properties on consoles, but mine was not defined.

Tell me where we’re wrong.

App.js

import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Detail from "../routes/Detail";
import Home from "../routes/Home";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/:id" element={<Detail />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Todos.js

import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { actionCreators } from '../store';

function Todo({ text, onBtnClick, id }) {
  return (
    <li>
      <Link to={`/${id}`}>{text}</Link>
      <button onClick={onBtnClick}>delete</button>
    </li>
  );
}

function mapDispatchToProps(dispatch, ownProps) {
  console.log(ownProps);
  return {
    onBtnClick: () => dispatch(actionCreators.deleteToDo(ownProps.id)),
  };
}

export default connect(null, mapDispatchToProps)(Todo); // 삭제는 state를 상관할 필요 x

Details.js

import React from "react";
import { connect } from "react-redux";

function Detail({ toDo }) {
  return (
    <>
      <h1>{toDo?.text}</h1>
      <h5>Created at: {toDo?.id}</h5>
    </>
  );
}

function mapStateToProps(state, ownProps) {
  console.log("ownProps: ", ownProps);
  const {
    match: {
      params: { id },
    },
  } = ownProps.match.params;
  console.log("id: ", id);
  return { toDos: state.find((toDo) => toDo.id === parseInt(id)) };
}

export default connect(mapStateToProps)(Detail);

Home.js

import React, { useState } from "react";
import { connect } from "react-redux";
import Todo from "../components/ToDo";
import { actionCreators } from "../store";

function Home({ toDos, addTodo }) {
  // we are creating props

  const [text, setText] = useState("");
  function onChange(e) {
    setText(e.target.value);
  }

  function onSubmit(e) {
    e.preventDefault();
    addTodo(text);
    console.log(text);
    setText("");
  }

  return (
    <>
      <h1>To Do</h1>
      <form onSubmit={onSubmit}>
        <input type="text" value={text} onChange={onChange} />
        <button>Add</button>
        <ul>
          {toDos.map((toDo) => (
            <Todo {...toDo} key={toDo.id} />
          ))}
        </ul>
      </form>
    </>
  );
}

function mapStateToProps(state) {
  return { toDos: state };
}

function mapDispatchToProps(dispatch) {
  return { addTodo: (text) => dispatch(actionCreators.addToDo(text)) };
}

export default connect(mapStateToProps, mapDispatchToProps)(Home);

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import App from "./components/App";
import store from "./store";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

store.js

import { createStore } from "redux";

const ADD = "ADD";
const DELETE = "DELETE";

const addToDo = (text) => {
  return {
    type: ADD,
    text,
  };
};

const deleteToDo = (id) => {
  return {
    type: DELETE,
    id: parseInt(id),
  };
};

const reducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [{ text: action.text, id: Date.now() }, ...state];
    case DELETE:
      return state.filter((toDo) => toDo.id !== action.id);
    default:
      return state;
  }
};

const store = createStore(reducer);

// store.subscribe(); 변화가 있으면 re-render하기 바람.

export const actionCreators = {
  addToDo,
  deleteToDo,
};

export default store;

I have no idea, personally I think I have followed below tutorial and router could not be same with them, the errors occurred.

https://nomadcoders.co/redux-for-beginners/lectures/1620

2

Answers


  1. You are using old React Redux code/patterns and react-router-dom doesn’t provide any props route props (e.g. there is no match prop) to components. Use the React hooks from both react-redux and react-router-dom to access the id route path parameter and redux state selections.

    Example:

    Detail

    <Route path="/:id" element={<Detail />} /> // no additional props passed
    
    import React from "react";
    import { useSelector } from "react-redux";
    import { useParams } from 'react-router-dom';
    
    function Detail() {
      const { id } = useParams();
      const todo = useSelector(state => state.find(todo => String(todo.id) === id));
    
      return (
        <>
          <h1>{todo?.text}</h1>
          <h5>Created at: {todo?.id}</h5>
        </>
      );
    }
    
    export default Detail;
    

    Home

    import React, { useState } from "react";
    import { useDispatch, useSelector } from "react-redux";
    import Todo from "../components/ToDo";
    import { actionCreators } from '../store';
    
    function Home() {
      const dispatch = useDispatch();
    
      const todos = useSelector(state => state);
    
      // we are creating props
    
      const [text, setText] = useState("");
      function onChange(e) {
        setText(e.target.value);
      }
    
      function onSubmit(e) {
        e.preventDefault();
        dispatch(actionCreators.addTodo(text));
        setText("");
      }
    
      return (
        <>
          <h1>To Do</h1>
          <form onSubmit={onSubmit}>
            <input type="text" value={text} onChange={onChange} />
            <button>Add</button>
            <ul>
              {todos.map((todo) => (
                <Todo {...todo} key={todo.id} />
              ))}
            </ul>
          </form>
        </>
      );
    }
    
    export default Home;
    

    Todo

    import React from 'react';
    import { useDispatch } from 'react-redux';
    import { Link } from 'react-router-dom';
    import { actionCreators } from '../store';
    
    function Todo({ text, id }) {
      const dispatch = useDispatch();
    
      const onBtnClick = () => {
        dispatch(actionCreators.deleteTodo(id));
      };
    
      return (
        <li>
          <Link to={`/${id}`}>{text}</Link>
          <button onClick={onBtnClick}>delete</button>
        </li>
      );
    }
    
    export default Todo;
    

    Edit nifty-rain-1r8gdw

    Login or Signup to reply.
  2. Drew’s answer detailing how to use the updated pattern would be the preferred solution, but if for some reason this is not feasible and you are on at least React 16.8+ you can recreate a version of the withRouter HOC.

    This is detailed in the React Router FAQ, but for completeness the code snippet is as follows:

    import {
      useLocation,
      useNavigate,
      useParams,
    } from "react-router-dom";
    
    function withRouter(Component) {
      function ComponentWithRouterProp(props) {
        let location = useLocation();
        let navigate = useNavigate();
        let params = useParams();
        return (
          <Component
            {...props}
            router={{ location, navigate, params }}
          />
        );
      }
    
      return ComponentWithRouterProp;
    }
    

    When using the withRouter HOC along side the connect HOC you will need to ensure they are used in the correct order to allow for the the Redux mapping functions to access the router properties.

    For example:

    function mapStateToProps(state, ownProps) {
      console.log("ownProps: ", ownProps);
      const { id } = ownProps.router.params;
      console.log("id: ", id);
      return { toDos: state.find((toDo) => toDo.id === parseInt(id)) };
    }
    
    export default withRouter(connect(mapStateToProps)(Detail));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search